Compare commits

..

73 Commits

Author SHA1 Message Date
Cory LaViska
2264257093 fix wa-pill and wa-input[pill] styles 2025-03-12 12:11:12 -04:00
Lea Verou
e5c2884880 [Tooltip] Specify inherited CSS properties on host, fixes #773 (#774)
* [Tooltip] Specify inherited CSS properties on host, fixes #773

* Remove unused `--show-delay` and `--hide-delay`
2025-03-10 15:08:27 -04:00
Lea Verou
1d600a77c4 Fix #566 2025-03-10 14:15:06 -04:00
lindsaym-fa
db3c568ba2 Add generated orange to Anodized palette 2025-03-05 22:27:11 -05:00
lindsaym-fa
4bb9805ba6 Add generated orange to Bright palette 2025-03-05 22:27:11 -05:00
lindsaym-fa
bd935fa8d5 Add generated orange to Classic palette 2025-03-05 22:27:11 -05:00
lindsaym-fa
c3e582b47b Add generated orange to Natural palette 2025-03-05 22:27:11 -05:00
lindsaym-fa
4d094a4e19 Add generated orange to Rudimentary palette 2025-03-05 22:27:11 -05:00
lindsaym-fa
782c404bdf Add generated orange to Default palette 2025-03-05 22:27:11 -05:00
lindsaym-fa
f1438981b2 Add generated orange to Elegant palette 2025-03-05 22:27:11 -05:00
lindsaym-fa
18b88c2f5c Add generated orange to Mild palette 2025-03-05 22:27:11 -05:00
lindsaym-fa
a2d85f49a3 Add generated orange to Vogue palette 2025-03-05 22:27:11 -05:00
Lea Verou
be00026cd3 Update postprocess.js 2025-03-05 22:27:11 -05:00
Lea Verou
58ed88bc5a Add orange to list of hues 2025-03-05 22:27:11 -05:00
Lea Verou
1d14e186f3 Generate missing hues from neighboring hues 2025-03-05 22:27:11 -05:00
Lea Verou
5f672aabc2 Refactor: variable rename for consistency 2025-03-05 22:27:11 -05:00
Lea Verou
db08e12a32 Pave the way for being able to have core colors that are not mapped to any tint 2025-03-05 22:27:11 -05:00
Lea Verou
e0fc639226 Only use hex when color is within sRGB 2025-03-05 22:27:11 -05:00
Lea Verou
e6c662b543 tintless.js -> postprocess.js 2025-03-05 22:27:11 -05:00
lindsaym-fa
d1de9a9a73 Fix incorrect sizing tokens in size utilities 2025-02-26 01:01:39 -05:00
lindsaym-fa
4931de8eb4 Fix text color for filled appearance 2025-02-26 01:01:39 -05:00
Lea Verou
71e7227763 Theme remixing fix: Order of params should not matter (#772)
Also renamed the `theme` export to `getThemeCode` since it was being renamed everywhere it was imported.
2025-02-21 14:03:55 -05:00
Lea Verou
dd671e15aa Changelog (#770) 2025-02-21 13:14:19 -05:00
Cory LaViska
2daeea0349 3.0.0-alpha.11 2025-02-21 12:53:05 -05:00
Cory LaViska
3cb6625c1d update changelog 2025-02-21 12:52:51 -05:00
Lea Verou
c4b5446d01 Fix boundingClientRect issue for elements whose host is display: contents 2025-02-21 12:02:20 -05:00
Lindsay M
41affca083 Allow color tweak tags to wrap (#769) 2025-02-21 11:50:13 -05:00
Lea Verou
132dbfabcc Gray tweaks prototype (#761)
Co-authored-by: Lindsay M <126139086+lindsaym-fa@users.noreply.github.com>
2025-02-20 12:10:43 -05:00
lindsaym-fa
4fc6224464 Fix missing listbox border 2025-02-19 14:54:11 -05:00
Lea Verou
4921d1c32e Save palette MVP, fixes #746 (#755) 2025-02-18 16:11:40 -05:00
Lindsay M
54d71d2319 Use tintless and clamped brand colors in themes (#754)
* Use tintless `brand` colors, cutoffs in themes

* Re-add `40-min`, add `70-max`

* Fix mistakes in Mellow theme

* Revert accidental Premium brand color change

* Add changelog
2025-02-18 10:22:32 -05:00
Cory LaViska
c1ecca0169 fix select hint (#760) 2025-02-18 15:09:52 +00:00
Lea Verou
d6a91919e0 Code block improvements
- Add ids, use ids to link copy button. No need for client-side script or updating the copy button manually for dynamic code snippets.
- Add button to link to code block
- Slight refactor on copy plugin to use the 11ty API properly
2025-02-14 15:09:02 -05:00
Lea Verou
4621094ea1 [Tab] Avoid sprouting attributes in the constructor 2025-02-14 13:04:39 -05:00
Lea Verou
726dc73e2a Hue tweaking & chroma scaling, closes #669 #670 (#747)
- General infrastructure to support palette tweaking
- Hue shifts per color scale (UI, permalinks, dynamic code snippets)
- Scale overall chroma up/down (UI, permalinks, dynamic code snippets)
- Update contrast ratio tables (styling for contrast up/down/fail could use improvement, but it's a starting point)
- Make sure it works with Turbo (i.e. things don't break when we navigate to another page)
2025-02-13 19:28:20 -05:00
Lea Verou
4bfebf3249 Improve color ranges script (#752) 2025-02-13 18:15:47 -05:00
Lea Verou
99ad0abdd3 Palette icons, take 4 2025-02-13 10:52:40 -05:00
Cory LaViska
902e2b6367 Fix invalid CSS property in <wa-select> (#751)
* remove empty selectors

* fix invalid property

* update changelog
2025-02-13 15:45:57 +00:00
Lea Verou
7f3210b12d Update changelog 2025-02-13 09:44:27 -05:00
Lea Verou
eee63bdecd Clamped brand color tokens (#741) 2025-02-12 15:13:03 -05:00
Lindsay M
a91fd07ed7 Add pink, closes #658 (#742) 2025-02-11 10:52:51 -05:00
Lindsay M
34aa8917a6 Revisit default palette, closes #706 (#731)
* Adjust  everything  (but not grays)

* Update keys

* Tweak green

* Update lighter `green` tints
2025-02-10 22:37:14 -05:00
Lindsay M
715c2c0def Revisit bright palette, closes #697 (#705)
* Adjust red and yellow

* Update remaining colors

* Correct Awesome theme success colors

* Adjust `red`

* Redefine `indigo` and `purple`

* Tweak `indigo`
2025-02-10 17:11:34 -05:00
Lea Verou
13b5385633 Radio improvements
- Drop `base` part (rel #207)
- Add `hint` slot and attribute (it was the only form control that did not support that)
- Refactor `@watch` calls to `updated()` per @claviska's plan for them
2025-02-10 14:19:53 -05:00
Lea Verou
d25f3748c4 Analyze color components (#732)
Also refactored existing color scripts (moved to separate directory, extracted utils to separate file)
2025-02-10 13:54:54 -05:00
Lindsay M
b6620ddf7e Revisit vogue palette, closes #723 (#738) 2025-02-10 13:23:57 -05:00
Lindsay M
d70d4a91b1 Revisit mild palette, closes #720 (#737)
* Adjust `cyan`, `indigo`, and `purple`

* Skew `indigo` more towards blue

* Better matching to Material tonal palettes
2025-02-10 13:20:44 -05:00
Lindsay M
bb1f7b2b7a Adjust everything (#734) 2025-02-10 11:42:31 -05:00
Lindsay M
9921c17d63 Revisit natural palette, closes #721 (#735)
* Adjust `cyan`

* Adjust `blue` and `purple`

* Adjust `red` and `yellow`
2025-02-10 11:42:11 -05:00
Lindsay M
7f964f9b56 Adjust everything (#736) 2025-02-10 11:41:36 -05:00
Lindsay M
31eeea1630 Revisit anodized palette, closes #718 (#733)
* Adjust  everything 

* Shift `red` towards orange in darker tints

* Tweak `green` balance
2025-02-10 11:41:17 -05:00
Lea Verou
ffda52a7cf Theme remixing: Dynamic code snippets (#729) 2025-02-07 23:03:34 -05:00
Lea Verou
9be7919c60 Implement brand stylesheets for missing hues 2025-02-07 17:59:18 -05:00
Lea Verou
c30f3c4b09 Improve theme remixing UI (#724)
Co-authored-by: lindsaym-fa <dev@lindsaym.design>
Co-authored-by: Lindsay M <126139086+lindsaym-fa@users.noreply.github.com>
2025-02-07 11:35:12 -05:00
Lea Verou
7e8f13b5cb Add accent color tint as separate variable (#725)
Co-authored-by: lindsaym-fa <dev@lindsaym.design>
2025-02-06 18:30:43 -05:00
Lindsay M
97ab986425 Revise classic palette, closes #707 (#716)
* Change  everything 

* No changes to `gray`
2025-02-06 17:58:33 -05:00
Lea Verou
f1fa7f713e Add label attribute to option & menu-item (#704)
* Add `label` attribute and `defaultLabel` getter to option and menu-item, drop `getTextLabel()`

- Add `label` attribute to option and menu-item, closes #698
- [Option] Add `defaultLabel` cached property to both
- [Option] Remove `getTextLabel()` method from both (people can just set `el.label`)
- Refactor: Merge utils to get text content into one more powerful utility function
- Fixed typo in `slot.ts`

.

Add `label` attribute to menu, remove `getTextLabel`

* Update filename
2025-02-06 10:58:09 -05:00
lindsaym-fa
3394a95057 Remove visual test failure markers 2025-02-06 10:57:33 -05:00
Lea Verou
eed9ddb9b3 Fix #622 2025-02-06 10:57:33 -05:00
Lea Verou
daf5e7734d Support opting out of anchor headings with data-no-anchor 2025-02-06 10:56:56 -05:00
Lea Verou
63133d0e33 Card fixes 2025-02-06 10:25:02 -05:00
Lea Verou
b2d74145b8 Remove stray console.log() 2025-02-05 11:35:55 -05:00
Lea Verou
17eeccbc5d Better infrastructure for core colors, show core colors in docs, closes #668 2025-02-05 11:24:13 -05:00
Lea Verou
68f645e776 [Option] Drop base part, rel #207 2025-02-05 09:29:50 -05:00
Cory LaViska
b3e4c59197 add secure context notice (#683) 2025-02-04 16:27:35 +00:00
Lindsay M
6080a85035 Replace teal with cyan, fixes #659 (#671)
Co-authored-by: Lea Verou <lea@verou.me>
2025-02-03 19:01:23 -05:00
lindsaym-fa
f7e088c8df Rename violet to purple, fixes #656 2025-02-03 18:47:03 -05:00
Lea Verou
80e1c05b37 Add --wa-color-brand-* tokens, fixes #655 (#662) 2025-02-03 15:39:59 -05:00
Cory LaViska
99f0273783 fix placeholder token (#652) 2025-02-03 17:03:34 +00:00
Cory LaViska
8e13683a30 Select tag fix (#651)
* add tag-counter part to wa-select

* on second thought
2025-02-03 16:48:46 +00:00
Cory LaViska
fd73542d2c restore slot detection for non-SSR environments (#649) 2025-02-03 16:21:53 +00:00
Cory LaViska
bde0ed7403 add orientation example and use utils for spacing (#648) 2025-02-03 15:46:51 +00:00
Cory LaViska
8dc49f7119 fix event listeners (#647) 2025-02-03 10:07:21 -05:00
127 changed files with 5031 additions and 1577 deletions

View File

@@ -13,3 +13,4 @@ package-lock.json
tsconfig.json
cdn
_site
docs/assets/scripts/prism-downloaded.js

View File

@@ -94,7 +94,7 @@ export default function (eleventyConfig) {
eleventyConfig.addPlugin(highlightCodePlugin());
// Add copy code buttons to code blocks
eleventyConfig.addPlugin(copyCodePlugin());
eleventyConfig.addPlugin(copyCodePlugin);
// Various text replacements
eleventyConfig.addPlugin(

1
docs/_data/hueRanges.js Normal file
View File

@@ -0,0 +1 @@
export { hueRanges as default } from '../assets/scripts/tweak/data.js';

View File

@@ -1 +1 @@
["red", "yellow", "green", "teal", "blue", "indigo", "violet", "gray"]
["red", "orange", "yellow", "green", "cyan", "blue", "indigo", "purple", "pink", "gray"]

View File

@@ -1 +1 @@
export { default as default } from '../../src/styles/color/palettes.js';
export { default as default } from '../../src/styles/color/scripts/palettes-analyzed.js';

View File

@@ -5,16 +5,18 @@
<meta name="theme-color" content="#f36944">
<script type="module" src="/assets/scripts/code-examples.js"></script>
<script type="module" src="/assets/scripts/copy-code.js"></script>
<script type="module" src="/assets/scripts/scroll.js"></script>
<script type="module" src="/assets/scripts/turbo.js"></script>
<script type="module" src="/assets/scripts/search.js"></script>
<script type="module" src="/assets/scripts/outline.js"></script>
{% if hasSidebar %}<script type="module" src="/assets/scripts/sidebar-tweaks.js"></script>{% endif %}
<script defer data-domain="backers.webawesome.com" src="https://plausible.io/js/script.js"></script>
{# Docs styles #}
<link rel="stylesheet" href="/assets/styles/docs.css" />
{% block head %}{% endblock %}
</head>
<body class="layout-{{ layout | stripExtension }}{{ ' page-wide' if wide }}">
<!-- use view="desktop" as default to reduce layout jank on desktop site. -->

View File

@@ -1,4 +1,4 @@
<table class="colors wa-palette-{{ paletteId }}">
<table class="colors wa-palette-{{ paletteId }} contrast-table" data-min-contrast="{{ minContrast }}">
<thead>
<tr>
<th></th>
@@ -12,19 +12,31 @@
</tr>
</thead>
{% for hue in hues -%}
<tr>
<tr data-hue="{{ hue }}">
<th>{{ hue | capitalize }}</th>
{% for tint_bg in tints -%}
{% set color_bg = palettes[paletteId][hue][tint_bg] %}
{% for tint_fg in tints | reverse -%}
{% set color_fg = palettes[paletteId][hue][tint_fg] %}
{% if (tint_fg - tint_bg) | abs == difference %}
<td>
<div class="color swatch" style="background-color: var(--wa-color-{{ hue }}-{{ tint_bg }}); color: var(--wa-color-{{ hue }}-{{ tint_fg }})">
{% set contrast_wcag = '' %}
{% if color_fg and color_bg %}
{% set contrast_wcag = color_bg.contrast(color_fg, 'WCAG21') %}
{% endif %}
{% set contrast_wcag = '' %}
{% if color_fg and color_bg -%}
{% set contrast_wcag = color_bg.contrast(color_fg, 'WCAG21') %}
{%- endif %}
<td v-for="contrast of [contrasts.{{ hue }}['{{ tint_bg }}']['{{ tint_fg }}']]"
data-tint-bg="{{ tint_bg }}" data-tint-fg="{{ tint_fg }}" data-original-contrast="{{ contrast_wcag }}">
<div v-content:number="contrast.value"
class="color swatch" :class="{
'value-up': contrast.value - contrast.original > 0.0001,
'value-down': contrast.original - contrast.value > 0.0001,
'contrast-fail': contrast.value < {{ minContrast }}
}"
style="--color: var(--wa-color-{{ hue }}-{{ tint_bg }}); color: var(--wa-color-{{ hue }}-{{ tint_fg }})"
:style="{
'--color': contrast.bgColor,
color: contrast.fgColor,
}"
>
{% if contrast_wcag %}
{{ contrast_wcag | number({maximumSignificantDigits: 2}) }}
{% else %}

View File

@@ -1,4 +1,4 @@
<wa-tab-group>
<wa-tab-group class="import-stylesheet-code">
<wa-tab panel="html">In HTML</wa-tab>
<wa-tab panel="css">In CSS</wa-tab>
<wa-tab-panel name="html">

View File

@@ -5,6 +5,9 @@
{% include "svgs/" + (page.data.icon or "thumbnail-placeholder") + ".njk" %}
</div>
<span class="page-name">{{ page.data.title }}</span>
{% if pageSubtitle -%}
<div class="wa-caption-s">{{ pageSubtitle }}</div>
{%- endif %}
</wa-card>
</a>
{% endif %}

View File

@@ -1,11 +1,14 @@
{% set paletteId = page.fileSlug %}
{% set tints = [80, 60, 40, 20] %}
{% set paletteId = palette.fileSlug or page.fileSlug %}
{% set suffixes = ['-80', '', '-20'] %}
{% set width = 20 %}
{% set height = 13 %}
{% set gap_x = 3 %}
{% set gap_y = 3 %}
{% set height = 12 %}
{% set height_core = 20 %}
{% set gap_x = 4 %}
{% set gap_y = 4 %}
<svg viewBox="0 0 {{ (width + gap_x) * hues|length }} {{ (height + gap_y) * tints|length }}" fill="none" xmlns="http://www.w3.org/2000/svg" class="wa-palette-{{ paletteId }} palette-icon">
{% set total_width = (width + gap_x) * hues|length %}
{% set total_height = (height + gap_y) * suffixes|length + (height_core - height) %}
<svg viewBox="0 0 {{ total_width }} {{ total_height }}" fill="none" xmlns="http://www.w3.org/2000/svg" class="wa-palette-{{ paletteId }} palette-icon">
<style>
@import url('/dist/styles/color/{{ paletteId }}.css') layer(palette.{{ paletteId }});
.palette-icon {
@@ -15,10 +18,14 @@
{% for hue in hues -%}
{% set hueIndex = loop.index0 %}
{% for tint in tints -%}
<rect x="{{ hueIndex * (width + gap_x) }}" y="{{ loop.index0 * (height + gap_y) }}"
width="{{ width }}" height="{{ height }}"
fill="var(--wa-color-{{ hue }}-{{ tint }})" rx="4" />
{% set y = 0 %}
{% for suffix in suffixes -%}
{% set swatch_height = height if suffix else height_core %}
<rect x="{{ hueIndex * (width + gap_x) }}" y="{{ y }}"
width="{{ width }}" height="{{ swatch_height }}"
fill="var(--wa-color-{{ hue }}{{ suffix }})" rx="2" />
{% set y = y + swatch_height + gap_y %}
{%- endfor %}
{% endfor %}
</svg>

View File

@@ -0,0 +1,24 @@
{% set themeId = theme.fileSlug %}
<div>
<template shadowrootmode="open">
<link rel="stylesheet" href="/dist/styles/utilities.css">
<link rel="stylesheet" href="/dist/styles/themes/{{ page.fileSlug or 'default' }}.css">
<link rel="stylesheet" href="/dist/styles/themes/{{ themeId }}/color.css">
<link rel="stylesheet" href="/assets/styles/theme-icons.css">
<div class="theme-color-icon wa-theme-{{ themeId }}">
<div class="wa-brand wa-accent">A</div>
<div class="wa-brand wa-outlined">A</div>
<div class="wa-brand wa-filled">A</div>
<div class="wa-brand wa-plain">A</div>
{# <div class="wa-danger wa-outlined wa-filled"><wa-icon slot="icon" name="circle-exclamation" variant="regular"></wa-icon></div> #}
<div class="wa-neutral wa-accent">A</div>
<div class="wa-neutral wa-outlined">A</div>
<div class="wa-neutral wa-filled">A</div>
<div class="wa-neutral wa-plain">A</div>
{# <div class="wa-warning wa-outlined wa-filled"><wa-icon slot="icon" name="triangle-exclamation" variant="regular"></wa-icon></div> #}
</div>
</template>
</div>

View File

@@ -0,0 +1,16 @@
{% set themeId = theme.fileSlug %}
<div>
<template shadowrootmode="open">
<link rel="stylesheet" href="/dist/styles/native/content.css">
<link rel="stylesheet" href="/dist/styles/native/blockquote.css">
<link rel="stylesheet" href="/dist/styles/themes/{{ page.fileSlug or 'default' }}.css">
<link rel="stylesheet" href="/dist/styles/themes/{{ themeId }}/typography.css">
<link rel="stylesheet" href="/assets/styles/theme-icons.css">
<div class="theme-typography-icon wa-theme-{{ themeId }}" data-no-outline data-no-anchor role="presentation">
<h3>Title</h3>
<p>Body text</p>
</div>
</template>
</div>

View File

@@ -2,7 +2,7 @@
<path fill-rule="evenodd" clip-rule="evenodd" d="M48.8665 59.1993L33.7329 33H64C63.8352 38.2705 62.3694 43.4244 59.7264 48C57.0833 52.5756 53.3507 56.4213 48.8665 59.1993Z" fill="var(--wa-color-green-70)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M32 64C26.714 64 21.5155 62.6919 16.8665 60.1993L32 34L47.1336 60.1993C42.4845 62.6919 37.286 64 32 64Z" fill="var(--wa-color-blue-60)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.27365 48C1.63063 43.4244 0.164839 38.2706 0 33L30.2671 33L15.1336 59.1993C10.6493 56.4213 6.91666 52.5756 4.27365 48Z" fill="var(--wa-color-indigo-60)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.27365 16C6.91667 11.4244 10.6493 7.57868 15.1336 4.80072L30.2671 31L0 31C0.16484 25.7295 1.63063 20.5756 4.27365 16Z" fill="var(--wa-color-violet-60)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.27365 16C6.91667 11.4244 10.6493 7.57868 15.1336 4.80072L30.2671 31L0 31C0.16484 25.7295 1.63063 20.5756 4.27365 16Z" fill="var(--wa-color-purple-60)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M32 30L16.8665 3.80072C21.5155 1.30813 26.714 0 32 0C37.286 0 42.4845 1.30813 47.1336 3.80072L32 30Z" fill="var(--wa-color-red-60)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M33.7329 31L48.8665 4.80072C53.3507 7.57868 57.0833 11.4244 59.7264 16C62.3694 20.5756 63.8352 25.7294 64 31L33.7329 31Z" fill="var(--wa-color-yellow-80)"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -23,7 +23,7 @@
</div>
<wa-divider></wa-divider>
<div class="wa-flank">
<wa-avatar shape="rounded" style="--size: 3em; --background-color: var(--wa-color-teal-60); --text-color: var(--wa-color-teal-95);">
<wa-avatar shape="rounded" style="--size: 3em; --background-color: var(--wa-color-cyan-60); --text-color: var(--wa-color-cyan-95);">
<wa-icon slot="icon" name="robot-astromech" family="duotone" style="font-size: 1.5em;"></wa-icon>
</wa-avatar>
<div class="wa-stack wa-gap-2xs">

View File

@@ -93,7 +93,7 @@
</td>
</tr>
<tr>
<th class="test-failure"><code>outlined</code></th>
<th><code>outlined</code></th>
<td>
<div class="wa-cluster wa-gap-2xs">
<wa-badge variant="brand" appearance="outlined">Brand</wa-badge>
@@ -495,7 +495,7 @@
</td>
</tr>
<tr>
<th class="test-failure"><code>outlined</code></th>
<th><code>outlined</code></th>
<td>
<div class="wa-grid wa-gap-2xs">
<wa-callout variant="brand" appearance="outlined">
@@ -696,7 +696,7 @@
</td>
</tr>
<tr>
<th class="test-failure"><code>outlined</code></th>
<th><code>outlined</code></th>
<td>
<div class="wa-cluster wa-gap-2xs">
<wa-tag variant="brand" appearance="outlined">Brand</wa-tag>

View File

@@ -1,31 +1,186 @@
{% set hasSidebar = true %}
{% set hasOutline = true %}
{# {% set forceTheme = page.fileSlug %} #}
{% extends '../_layouts/block.njk' %}
{% set paletteId = page.fileSlug %}
{% block afterContent %}
<style>@import url('/dist/styles/color/{{ paletteId }}.css') layer(palette.{{ paletteId }});</style>
{% set tints = ["95", "90", "80", "70", "60", "50", "40", "30", "20", "10", "05"] %}
<table class="colors wa-palette-{{ paletteId }}">
{% extends '../_includes/base.njk' %}
{% block head %}
<style>@import url('/dist/styles/color/{{ paletteId }}.css') layer(palette.{{ paletteId }});</style>
<link href="{{ page.url }}../tweak.css" rel="stylesheet">
<script type="module" src="{{ page.url }}../tweak.js"></script>
{% endblock %}
{% block header %}
<div id="palette-app" data-palette-id="{{ paletteId }}">
<div
:class="{
tweaking: tweaking.chroma,
'tweaking-chroma': tweaking.chroma,
'tweaking-hue': tweaking.chroma,
'tweaking-gray-chroma': tweaking.grayChroma,
'tweaked-chroma': tweaked?.chroma,
'tweaked-hue': tweaked?.hue,
'tweaked-any': tweaked
}"
:style="{
'--chroma-scale': chromaScale,
'--gray-chroma': tweaked?.grayChroma ? grayChroma : '',
}">
{% include 'breadcrumbs.njk' %}
<h1 v-if="saved" class="title">
{% raw %}{{ saved.title }}{% endraw %}
<wa-icon-button name="pencil" label="Rename palette" @click="rename"></wa-icon-button>
<wa-icon-button class="delete" name="trash" label="Delete palette" @click="deleteSaved"></wa-icon-button>
</h1>
<h1 v-if="!saved" class="title">{{ title }}</h1>
<div class="block-info">
<code class="class">.wa-palette-{{ paletteId }}</code>
{% include '../_includes/status.njk' %}
{% if not isPro %}
<wa-badge class="pro" v-if="tweaked">PRO</wa-badge>
{% endif %}
</div>
{% if description %}
<p class="summary">
{{ description | inlineMarkdown | safe }}
</p>
{% endif %}
{% endblock %}
{% block afterContent %}
{% set maxChroma = 0 %}
<wa-callout size="small" class="tweaked-callout" variant="warning">
<wa-icon name="sliders-simple" slot="icon" variant="regular"></wa-icon>
This palette has been tweaked.
<div class="wa-cluster wa-gap-xs">
<wa-tag v-for="tweakHumanReadable, param in tweaksHumanReadable" removable @wa-remove="reset(param)">{% raw %}{{ tweakHumanReadable }}{% endraw %}</wa-tag>
</div>
<wa-button @click="reset()" appearance="outlined" variant="danger">
<span slot="prefix" class="icon-modifier">
<wa-icon name="circle-xmark" variant="regular"></wa-icon>
</span>
Reset
</wa-button>
<wa-button v-if="!saved" @click="save" variant="success">
<span slot="prefix" class="icon-modifier">
<wa-icon name="sidebar" variant="regular"></wa-icon>
<wa-icon name="circle-plus" class="modifier" style="color: light-dark(var(--wa-color-green-70), var(--wa-color-green-60));"></wa-icon>
</span>
Save
</wa-button>
</wa-callout>
<table class="colors main wa-palette-{{ paletteId }}">
<thead>
<tr>
<th></th>
<th class="core-column">Core tint</th>
{% for tint in tints -%}
<th>{{ tint }}</th>
{%- endfor %}
</tr>
</thead>
{# Initialize to last hue before gray #}
{%- set hueBefore = hues[hues|length - 2] -%}
{% for hue in hues -%}
<tr>
<th>{{ hue | capitalize }}</th>
{% set coreTint = palettes[paletteId][hue].maxChromaTint %}
{%- set coreColor = palettes[paletteId][hue][coreTint] -%}
{%- set maxChroma = coreColor.c if coreColor.c > maxChroma else maxChroma -%}
{% if hue === 'gray' %}
<tr data-hue="{{ hue }}" class="color-scale"
:class="{tweaking: tweaking.grayChroma, tweaked: tweaked.grayChroma || tweaked.grayColor }">
{% else %}
<tr data-hue="{{ hue }}" class="color-scale"
:class="{tweaking: tweaking.{{ hue }}, tweaked: hueShifts.{{ hue }} }"
:style="{ '--hue-shift': hueShifts.{{ hue }} || '' }">
{% endif %}
<th>
{{ hue | capitalize }}
</th>
<td class="core-column"
style="--color: var(--wa-color-{{ hue }})"
:style="{
'--color-tweaked': colors.{{ hue }}[{{ coreTint }}],
'--color-gray-undertone': colors[grayColor][{{coreTint}}],
'--color-tweaked-no-gray-chroma': colorsMinusGrayChroma.{{ hue }}[{{ coreTint }}],
}">
<wa-dropdown>
<div slot="trigger" id="core-{{ hue }}-swatch" data-tint="core" class="color swatch"
style="background-color: var(--wa-color-{{ hue }}); color: var(--wa-color-{{ hue }}-{{ '05' if palettes[paletteId][hue].maxChromaTint > 60 else '95' }});"
>
{{ palettes[paletteId][hue].maxChromaTint }}
<wa-icon name="sliders-simple" class="tweak-icon"></wa-icon>
</div>
<div class="popup">
{% if hue === 'gray' %}
<wa-radio-group class="core-color" orientation="horizontal" v-model="grayColor">
{% for h in hues -%}
{%- if h !== 'gray' -%}
<wa-radio-button id="gray-undertone-{{ h }}" value="{{ h }}" label="{{ h | capitalize }}" style="--color: var(--wa-color-{{ h }})"></wa-radio-button>
<wa-tooltip for="gray-undertone-{{ h }}" hoist>
{{ h | capitalize }}
</wa-tooltip>
{%- endif -%}
{%- endfor -%}
<div slot="label">
Gray undertone
</div>
</wa-radio-group>
<div class="decorated-slider gray-chroma-slider" :style="{'--max': maxGrayChroma}">
<wa-slider name="gray-chroma" v-model="grayChroma" ref="grayChromaSlider"
value="0" min="0" :max="maxGrayChroma" step="0.01"
@input="tweaking.grayChroma = true" @change="tweaking.grayChroma = false">
<div slot="label">
Gray colorfulness
<wa-icon-button @click="grayChroma = originalGrayChroma" class="clear-button" name="circle-xmark" library="system" variant="regular" label="Reset"></wa-icon-button>
</div>
</wa-slider>
<div class="label-min">Neutral</div>
<div class="label-max" v-content="moreHue[grayColor]">Warmer/Cooler</div>
</div>
{% else %}
{%- set hueAfter = hues[loop.index0 + 1] -%}
{%- set hueAfter = hues[0] if hueAfter == 'gray' else hueAfter -%}
{%- set minShift = hueRanges[hue].min - coreColor.h | round -%}
{%- set maxShift = hueRanges[hue].max - coreColor.h | round -%}
<div class="decorated-slider hue-shift-slider" style="--min: {{ minShift }}; --max: {{ maxShift }};">
<wa-slider name="{{ hue }}-shift" v-model="hueShifts.{{ hue }}" value="0"
min="{{ minShift }}" max="{{ maxShift }}" step="1"
@input="tweaking.hue = tweaking.{{hue}} = true"
@change="tweaking.hue = tweaking.{{ hue }} = false">
<div slot="label">
Tweak {{ hue }} hue
<wa-icon-button @click="hueShifts.{{ hue }} = 0" class="clear-button" name="circle-xmark" library="system" variant="regular" label="Reset"></wa-icon-button>
</div>
</wa-slider>
<div class="label-min">More {{hueBefore}}</div>
<div class="label-max">More {{hueAfter}}</div>
</div>
{%- set hueBefore = hue -%}
{% endif %}
<div class="wa-gap-s">
<code>--wa-color-{{ hue }}</code>
<wa-copy-button value="--wa-color-{{ hue }}" copy-label="--wa-color-{{ hue }}"></wa-copy-button>
</div>
</div>`
</wa-dropdown>
</td>
{% for tint in tints -%}
<td>
<div class="color swatch" style="background-color: var(--wa-color-{{ hue }}-{{ tint }})">
{%- set color = palettes[paletteId][hue][tint] -%}
<td data-tint="{{ tint }}" style="--color: var(--wa-color-{{ hue }}-{{ tint }})"
:style="{
'--color-tweaked': colors.{{ hue }}[{{ tint }}],
'--color-tweaked-no-gray-chroma': colorsMinusGrayChroma.{{ hue }}[{{ tint }}],
}">
<div class="color swatch" style="--color: var(--wa-color-{{ hue }}-{{ tint }})">
<wa-copy-button value="--wa-color-{{ hue }}-{{ tint }}" copy-label="--wa-color-{{ hue }}-{{ tint }}"></wa-copy-button>
</div>
</td>
@@ -34,6 +189,26 @@
{%- endfor %}
</table>
{% set chromaScaleBounds = [
(0.08 / maxChroma) | number({maximumFractionDigits: 2}),
(0.3 / maxChroma]) | number({maximumFractionDigits: 2}) -%}
<div class="decorated-slider chroma-scale-slider wa-palette-{{ paletteId }}"
:class="{ tweaked: chromaScale !== 1 }"
style="--min: {{ chromaScaleBounds[0] }}; --max: {{ chromaScaleBounds[1] }};">
<wa-slider name="chroma-scale" ref="chromaScaleSlider"
v-model="chromaScale" value="1" step="0.01"
min="{{ chromaScaleBounds[0] }}" max="{{ chromaScaleBounds[1] }}"
@input="tweaking.chroma = true"
@change="tweaking.chroma = false">
<div slot="label">
Overall colorfulness
<wa-icon-button @click="chromaScale = 1" class="clear-button" name="circle-xmark" library="system" variant="regular" label="Reset"></wa-icon-button>
</div>
</wa-slider>
<div class="label-min">More muted</div>
<div class="label-max">More vibrant</div>
</div>
<h2>Used By</h2>
<section class="index-grid">
@@ -58,6 +233,7 @@ A difference of `40` ensures a minimum **3:1** contrast ratio, suitable for larg
{% endmarkdown %}
{% set difference = 40 %}
{% set minContrast = 3 %}
{% include "contrast-table.njk" %}
{% markdown %}
@@ -77,6 +253,7 @@ A difference of `50` ensures a minimum **4.5:1** contrast ratio, suitable for no
{% endmarkdown %}
{% set difference = 50 %}
{% set minContrast = 4.5 %}
{% include "contrast-table.njk" %}
{% markdown %}
@@ -95,6 +272,7 @@ A difference of `60` ensures a minimum **7:1** contrast ratio, suitable for all
{% endmarkdown %}
{% set difference = 60 %}
{% set minContrast = 7 %}
{% include "contrast-table.njk" %}
{% markdown %}
@@ -107,13 +285,34 @@ This also goes for a difference of `65`:
{% include "contrast-table.njk" %}
{% markdown %}
## How to use this palette
## How to use this palette { #usage }
If you are using a Web Awesome theme that uses this palette, it will already be included.
To use a different palette than a theme default, or to use it in a custom theme, you can import this palette directly from the Web Awesome CDN.
{% set stylesheet = 'styles/color/' + page.fileSlug + '.css' %}
{% include 'import-stylesheet-code.md.njk' %}
<wa-tab-group class="import-stylesheet-code">
<wa-tab panel="html">In HTML</wa-tab>
<wa-tab panel="css">In CSS</wa-tab>
<wa-tab-panel name="html">
Add the following code to the `<head>` of your page:
```html { v-content:html="code.html.highlighted" }
<link rel="stylesheet" href="{% cdnUrl stylesheet %}" />
```
</wa-tab-panel>
<wa-tab-panel name="css">
Add the following code at the top of your CSS file:
```css { v-content:html="code.css.highlighted" }
@import url('{% cdnUrl stylesheet %}');
```
</wa-tab-panel>
</wa-tab-group>
{% endmarkdown %}
</div></div> {# end palette app #}
{% endblock %}

View File

@@ -4,100 +4,143 @@
{% extends '../_includes/base.njk' %}
{% block head %}
<script>
globalThis.wa_data ??= {};
wa_data.baseTheme = "{{ page.fileSlug }}";
wa_data.themes = {
{% for theme in collections.theme -%}
"{{ theme.fileSlug }}": {
"title": "{{ theme.data.title }}",
"palette": "{{ theme.data.palette }}",
"brand": "{{ theme.data.brand }}"
},
{% endfor %}
};
wa_data.palettes = {
{% for palette in collections.palette -%}
"{{ palette.fileSlug }}": {
"title": "{{ palette.data.title }}",
},
{% endfor %}
};
</script>
<link href="{{ page.url }}../remix.css" rel="stylesheet">
<script src="{{ page.url }}../remix.js" type="module"></script>
{% endblock %}
{% block header %}
<iframe src='{{ page.url }}demo.html' id="demo"></iframe>
<p id="mix_and_match" class="wa-gap-m">
<strong>
<wa-icon name="merge" slot="prefix"></wa-icon>
Remix
<wa-icon-button href="#remixing" name="circle-question" slot="suffix" variant="regular" label="How to use?"></wa-icon-button>
</strong>
<wa-select name="colors" label="Colors from…" size="small">
<wa-details id="mix_and_match" class="wa-gap-m" >
<h4 slot="summary" data-no-anchor data-no-outline id="remix">
<wa-icon name="arrows-rotate"></wa-icon>
Remix this theme
<wa-icon id="what-is-remixing" href="#remixing" name="circle-question" slot="suffix" variant="regular"></wa-icon>
<wa-tooltip for="what-is-remixing">Customize this theme by changing its colors and/or remixing it with design elements from other themes!</wa-tooltip>
</h4>
<wa-select name="colors" label="Colors from…" value="" clearable>
<wa-icon name="palette" slot="prefix" variant="regular"></wa-icon>
<wa-option value="">(Theme default)</wa-option>
<wa-divider></wa-divider>
{% for theme in collections.theme | sort %}
{% if theme.fileSlug !== page.fileSlug %}
<wa-option value="{{ theme.fileSlug }}">{{ theme.data.title }}</wa-option>
{% endif %}
{% set currentTheme = theme.fileSlug == page.fileSlug %}
<wa-option label="{{ theme.data.title }}" value="{{ theme.fileSlug if not currentTheme }}" {{ (theme.fileSlug if currentTheme) | attr('data-id') }}>
<wa-card with-header>
<div slot="header">
{% include "svgs/theme-color.njk" %}
</div>
<span class="page-name">
{{ theme.data.title }}
{% if theme.data.isPro %}<wa-badge class="pro">PRO</wa-badge>{% endif %}
{% if currentTheme %}<wa-badge variant="neutral" appearance="outlined">This theme</wa-badge>{% endif %}
</span>
</wa-card>
</wa-option>
{% endfor %}
</wa-select>
<wa-select name="palette" label="Palette" size="small">
<wa-select name="palette" label="Palette" clearable>
<wa-icon name="swatchbook" slot="prefix" variant="regular"></wa-icon>
<wa-option value="">(Theme default)</wa-option>
<wa-divider></wa-divider>
{% for p in collections.palette | sort %}
{% if p.fileSlug !== palette %}
<wa-option value="{{ p.fileSlug }}">{{ p.data.title }}</wa-option>
{% endif %}
{% set defaultPalette = palette %}
{% for palette in collections.palette | sort %}
{% set currentPalette = palette.fileSlug == defaultPalette %}
<wa-option label="{{ palette.data.title }}" value="{{ palette.fileSlug if not currentPalette }}" {{ (palette.fileSlug if currentPalette) | attr('data-id') }}>
<wa-card with-header>
<div slot="header">
{% include "svgs/" + (palette.data.icon or "thumbnail-placeholder") + ".njk" %}
</div>
<span class="page-name">
{{ palette.data.title }}
{% if palette.data.isPro %}<wa-badge class="pro">PRO</wa-badge>{% endif %}
{% if currentPalette %}<wa-badge variant="neutral" appearance="outlined">Theme default</wa-badge>{% endif %}
</span>
</wa-card>
</wa-option>
{% endfor %}
{% set palette = defaultPalette %}
</wa-select>
<wa-select name="brand" label="Brand color" value="" clearable>
<div class="selected-swatch" slot="prefix"></div>
{% for hue in hues %}
{% set currentBrand = hue == brand %}
<wa-option label="{{ hue | capitalize }}" value="{{ hue if not currentBrand }}" {{ (hue if currentBrand) | attr('data-id') }} style="--color: var(--wa-color-{{ hue }})">
{{ hue | capitalize }}
{% if currentBrand %}<wa-badge variant="neutral" appearance="outlined">Theme default</wa-badge>{% endif %}
</wa-option>
{% endfor %}
</wa-select>
<wa-select name="typography" label="Typography from…" size="small">
<wa-select name="typography" label="Typography from…" clearable>
<wa-icon name="font-case" slot="prefix"></wa-icon>
<wa-option value="">(Theme default)</wa-option>
<wa-divider></wa-divider>
{% for theme in collections.theme | sort %}
{% if theme.fileSlug !== page.fileSlug %}
<wa-option value="{{ theme.fileSlug }}">{{ theme.data.title }}</wa-option>
{% endif %}
{% set currentTheme = theme.fileSlug == page.fileSlug %}
<wa-option label="{{ theme.data.title }}" value="{{ theme.fileSlug if not currentTheme }}" {{ (theme.fileSlug if currentTheme) | attr('data-id') }}>
<wa-card with-header>
<div slot="header">
{% include "svgs/theme-typography.njk" %}
</div>
<span class="page-name">
{{ theme.data.title }}
{% if theme.data.isPro %}<wa-badge class="pro">PRO</wa-badge>{% endif %}
{% if currentTheme %}<wa-badge variant="neutral" appearance="outlined">This theme</wa-badge>{% endif %}
</span>
</wa-card>
</wa-option>
{% endfor %}
</wa-select>
</wa-details>
</p>
<script>
document.querySelector('#mix_and_match').addEventListener('change', function(event) {
let selects = document.querySelectorAll('#mix_and_match wa-select');
let url = new URL(demo.src);
for (let select of selects) {
url.searchParams.set(select.name, select.value);
}
demo.src = url;
});
</script>
<h2>Default Color Palette</h2>
<h2>Color</h2>
{% set paletteURL = '/docs/palettes/' + palette + '/' %}
{% set themePage = page %}
{% set page = paletteURL | getCollectionItemFromUrl %}
<div class="index-grid">
{% include 'page-card.njk' %}
</div>
{% set page = themePage %}
<div class="index-grid">
{% set themePage = page %}
{% set page = paletteURL | getCollectionItemFromUrl %}
{% set pageSubtitle = "Default color palette" %}
{% include 'page-card.njk' %}
{% set page = themePage %}
<wa-card style="--header-background: var(--wa-color-{{ brand }})" class="wa-palette-{{ palette }}">
<div slot="header"></div>
<div class="page-name">{{ brand | capitalize }}</div>
<div class="wa-caption-s">Default brand color</div>
</wa-card>
</div>
{% endblock %}
{% block afterContent %}
{% markdown %}
## How to use this theme
<h2 id="usage">How to use this theme</h2>
{% markdown %}
You can import this theme from the Web Awesome CDN.
{% set stylesheet = 'styles/themes/' + page.fileSlug + '.css' %}
{% include 'import-stylesheet-code.md.njk' %}
### Remixing { #remixing }
If you want to combine the **colors** from this theme with another theme, you can import this CSS file *after* the other themes CSS file:
{% set stylesheet = 'styles/themes/' + page.fileSlug + '/color.css' %}
{% include 'import-stylesheet-code.md.njk' %}
To use the **typography** from this theme with another theme, you can import this CSS file *after* the other themes CSS file:
{% set stylesheet = 'styles/themes/' + page.fileSlug + '/typography.css' %}
{% include 'import-stylesheet-code.md.njk' %}
<wa-callout variant="warning">
<wa-icon slot="icon" name="triangle-exclamation" variant="regular"></wa-icon>
Please note that not all combinations will look good — once youre mixing and matching, youre on your own!
</wa-callout>
## Dark mode
To activate the dark color scheme of the theme on any element and its contents, apply the class `wa-dark` to it.

View File

@@ -37,7 +37,8 @@ export function anchorHeadingsPlugin(options = {}) {
}
// Look for headings
container.querySelectorAll(options.headingSelector).forEach(heading => {
let selector = `:is(${options.headingSelector}):not([data-no-anchor], [data-no-anchor] *)`;
container.querySelectorAll(selector).forEach(heading => {
const hasAnchor = heading.querySelector('a');
const existingId = heading.getAttribute('id');
const clone = parse(heading.outerHTML);

View File

@@ -3,30 +3,39 @@ import { parse } from 'node-html-parser';
/**
* Eleventy plugin to add copy buttons to code blocks.
*/
export function copyCodePlugin(options = {}) {
export function copyCodePlugin(eleventyConfig, options = {}) {
options = {
container: 'body',
...options,
};
return function (eleventyConfig) {
eleventyConfig.addTransform('copy-code', content => {
const doc = parse(content, { blockTextElements: { code: true } });
const container = doc.querySelector(options.container);
let codeCount = 0;
eleventyConfig.addTransform('copy-code', content => {
const doc = parse(content, { blockTextElements: { code: true } });
const container = doc.querySelector(options.container);
if (!container) {
return content;
if (!container) {
return content;
}
// Look for code blocks
container.querySelectorAll('pre > code').forEach(code => {
const pre = code.closest('pre');
let preId = pre.getAttribute('id') || `code-block-${++codeCount}`;
let codeId = code.getAttribute('id') || `${preId}-inner`;
if (!code.getAttribute('id')) {
code.setAttribute('id', codeId);
}
if (!pre.getAttribute('id')) {
pre.setAttribute('id', preId);
}
// Look for code blocks
container.querySelectorAll('pre > code').forEach(code => {
const pre = code.closest('pre');
// Add a copy button (we set the copy data at runtime to reduce page bloat)
pre.innerHTML = `<wa-copy-button class="copy-button" hoist></wa-copy-button>` + pre.innerHTML;
});
return doc.toString();
// Add a copy button
pre.innerHTML += `<wa-icon-button href="#${preId}" class="block-link-icon" name="link"></wa-icon-button>
<wa-copy-button from="${codeId}" class="copy-button" hoist></wa-copy-button>`;
});
};
return doc.toString();
});
}

View File

@@ -1,15 +0,0 @@
function setCopyValue() {
document.querySelectorAll('.copy-button').forEach(copyButton => {
const pre = copyButton.closest('pre');
const code = pre?.querySelector('code');
if (code) {
copyButton.value = code.textContent;
}
});
}
// Set data for all copy buttons when the page loads
setCopyValue();
document.addEventListener('turbo:load', setCopyValue);

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
globalThis.Prism = globalThis.Prism || {};
globalThis.Prism.manual = true;
await import('./prism-downloaded.js');
Prism.plugins.customClass.prefix('code-');
export default Prism;

View File

@@ -0,0 +1,254 @@
const sidebar = (globalThis.sidebar = {});
sidebar.palettes = {
render() {
if (this.saved.length === 0) {
return;
}
for (let palette of this.saved) {
sidebar.palette.render(palette);
}
sidebar.updateCurrent();
},
updateSaved() {
this.saved = localStorage.savedPalettes ? JSON.parse(localStorage.savedPalettes) : [];
},
save(saved = this.saved) {
this.saved = saved ?? [];
if (saved.length > 0) {
localStorage.savedPalettes = JSON.stringify(saved);
} else {
delete localStorage.savedPalettes;
}
},
};
sidebar.palettes.updateSaved();
addEventListener('storage', event => sidebar.palettes.updateSaved());
sidebar.palette = {
getUid() {
let savedPalettes = sidebar.palettes.saved;
let uids = new Set(savedPalettes.map(p => p.uid));
if (savedPalettes.length === 0) {
return 1;
}
// Find first available number
for (let i = 1; i <= savedPalettes.length + 1; i++) {
if (!uids.has(i)) {
return i;
}
}
},
equals(p1, p2) {
if (!p1 || !p2) {
return false;
}
return p1.id === p2.id && p1.uid === p2.uid;
},
delete(palette) {
let savedPalettes = sidebar.palettes.saved;
let count = savedPalettes.length;
if (count === 0) {
return;
}
// TODO improve UX of this
if (!confirm(`Are you sure you want to delete palette “${palette.title}”?`)) {
return;
}
savedPalettes = savedPalettes.filter(p => !sidebar.palette.equals(palette, p));
if (savedPalettes.length === count) {
// Nothing was removed
return;
}
// Update UI
let pathname = `/docs/palettes/${palette.id}/`;
let url = pathname + palette.search;
let uls = new Set();
for (let a of document.querySelectorAll(`#sidebar a[href="${url}"]`)) {
let li = a.closest('li');
let ul = li.closest('ul');
uls.add(ul);
li.remove();
}
// Remove empty lists
for (let ul of uls) {
if (!ul.children.length) {
ul.remove();
}
}
sidebar.updateCurrent();
sidebar.palettes.save(savedPalettes);
if (sidebar.palette.equals(globalThis.paletteApp?.saved, palette)) {
paletteApp.postDelete();
}
},
getSaved(palette, savedPalettes = sidebar.palettes.saved) {
return savedPalettes.find(p => sidebar.palette.equals(p, palette));
},
render(palette) {
// Find existing <a>
let { title, id, search, uid } = palette;
for (let a of document.querySelectorAll(`#sidebar a[href^="/docs/palettes/${id}/"][data-uid="${uid}"]`)) {
// Palette already in sidebar, just update it
a.textContent = palette.title;
a.href = `/docs/palettes/${id}/${search}`;
return;
}
let pathname = `/docs/palettes/${id}/`;
let url = pathname + search;
let parentA = document.querySelector(`a[href="${pathname}"]`);
let parentLi = parentA?.closest('li');
let a;
if (parentLi) {
a = Object.assign(document.createElement('a'), { href: url, textContent: title });
a.dataset.uid = uid;
let badges = [...parentLi.querySelectorAll('wa-badge')].map(badge => badge.cloneNode(true));
let ul = parentLi.querySelector('ul') ?? parentLi.appendChild(document.createElement('ul'));
let li = document.createElement('li');
let deleteButton = Object.assign(document.createElement('wa-icon-button'), {
name: 'trash',
label: 'Delete',
className: 'delete',
});
deleteButton.addEventListener('click', () => {
let palette = { id, uid, title: a.textContent, search: a.search };
sidebar.palette.delete(palette);
});
li.append(a, ' ', ...badges, deleteButton);
ul.appendChild(li);
}
},
save(palette, saved) {
let savedPalettes = sidebar.palettes.saved;
let existing = this.getSaved(saved ?? palette, savedPalettes);
let oldValues;
if (existing) {
// Rename
oldValues = { ...existing };
Object.assign(existing, palette);
} else {
savedPalettes.push(palette);
}
this.render(palette, oldValues);
sidebar.updateCurrent();
sidebar.palettes.save(savedPalettes);
},
};
sidebar.updateCurrent = function () {
// Find the sidebar link with the longest shared prefix with the current URL
let pathParts = location.pathname.split('/').filter(Boolean);
let prefixes = [];
if (pathParts.length === 1) {
// If at /docs/ we just use that, otherwise we want at least two parts (/docs/xxx/)
prefixes.push('/' + pathParts[0] + '/');
} else {
for (let i = 2; i <= pathParts.length; i++) {
prefixes.push('/' + pathParts.slice(0, i).join('/') + '/');
}
}
// Last prefix includes the search too (if any)
if (location.search) {
let params = new URLSearchParams(location.search);
params.sort();
prefixes.push(prefixes.at(-1) + location.search);
}
// We want to start from the longest prefix
prefixes.reverse();
let candidates;
let matchingPrefix;
for (let prefix of prefixes) {
candidates = document.querySelectorAll(`#sidebar a[href^="${prefix}"]`);
if (candidates.length > 0) {
matchingPrefix = prefix;
break;
}
}
if (!matchingPrefix) {
// Abort mission
return;
}
if (matchingPrefix === pathParts.at(-1)) {
// Full path matches, check search
if (location.search) {
candidates = [...candidates];
let searchParams = new URLSearchParams(location.search);
if (searchParams.has('uid')) {
// Only consider candidates with the same uid
candidates = candidates.filter(a => {
let params = new URLSearchParams(a.search);
return params.get('uid') === searchParams.get('uid');
});
} else {
// Sort candidates based on how many params they have in common, in descending order
candidates = candidates.sort((a, b) => {
return countSharedSearchParams(searchParams, b.search) - countSharedSearchParams(searchParams, a.search);
});
}
}
}
if (candidates.length > 0) {
for (let current of document.querySelectorAll('#sidebar a.current')) {
current.classList.remove('current');
}
candidates[0].classList.add('current');
}
};
sidebar.render = function () {
this.palettes.render();
};
sidebar.render();
window.addEventListener('turbo:render', () => sidebar.render());
function countSharedSearchParams(searchParams, search) {
if (!search || search === '?') {
return 0;
}
let params = new URLSearchParams(search);
return [...searchParams.keys()].filter(k => params.get(k) === searchParams.get(k)).length;
}

View File

@@ -0,0 +1,6 @@
/**
* Get import code for remixed themes and tweaked palettes.
*/
export { getThemeCode } from './tweak/code.js';
export { cdnUrl, hueRanges, hues, selectors, tints, urls } from './tweak/data.js';
export { default as Permalink } from './tweak/permalink.js';

View File

@@ -0,0 +1,54 @@
/**
* Get import code for remixed themes and tweaked palettes.
*/
import { urls } from './data.js';
export function cssImport(url, options = {}) {
let { language = 'html', cdnUrl = '/dist/', attributes } = options;
url = cdnUrl + url;
if (language === 'css') {
return `@import url('${url}');`;
} else {
attributes = attributes ? ` ${attributes}` : '';
return `<link rel="stylesheet" href="${url}"${attributes} />`;
}
}
export function cssLiteral(value, options = {}) {
let { language = 'html' } = options;
if (language === 'css') {
return value;
} else {
return `<style>\n${value}\n</style>`;
}
}
// Params in correct order
export const themeParams = ['colors', 'palette', 'brand', 'typography'];
export function getThemeCode(base, params, options) {
let ret = [];
if (base) {
ret.push(urls.theme(base));
}
for (let aspect of themeParams) {
let value = params[aspect];
if (value) {
ret.push(urls[aspect](value));
}
}
return ret.map(url => cssImport(url, options)).join('\n');
}
export function cssRule(selector, declarations, { indent = ' ' } = {}) {
selector = Array.isArray(selector) ? selector.flat().join(',\n') : selector;
declarations = Array.isArray(declarations) ? declarations.flat() : declarations;
declarations = declarations.map(declaration => indent + declaration.trim()).join('\n');
return `${selector} {\n${declarations.trimEnd()}\n}`;
}

View File

@@ -0,0 +1,73 @@
/**
* Data related to theme remixing and palette tweaking
* Must work in both browser and Node.js
*/
export const cdnUrl = globalThis.document ? document.documentElement.dataset.cdnUrl : '/dist/';
export const urls = {
theme: id => `styles/themes/${id}.css`,
colors: id => `styles/themes/${id}/color.css`,
palette: id => `styles/color/${id}.css`,
brand: id => `styles/brand/${id}.css`,
typography: id => `styles/themes/${id}/typography.css`,
};
export const selectors = {
palette: id =>
[':where(:root)', ':host', ":where([class^='wa-theme-'], [class*=' wa-theme-'])", `.wa-palette-${id}`].join(',\n'),
};
export const hueRanges = {
red: { min: 5, max: 35 }, // 30
orange: { min: 35, max: 60 }, // 25
yellow: { min: 60, max: 112 }, // 45
green: { min: 112, max: 170 }, // 55
cyan: { min: 170, max: 220 }, // 50
blue: { min: 220, max: 265 }, // 45
indigo: { min: 265, max: 290 }, // 25
purple: { min: 290, max: 320 }, // 30
pink: { min: 320, max: 365 }, // 45
};
export const moreHue = {
red: 'Redder',
orange: 'More orange', // https://www.reddit.com/r/grammar/comments/u9n0uo/is_it_oranger_or_more_orange/
yellow: 'Yellower',
green: 'Greener',
cyan: 'More cyan',
blue: 'Bluer',
indigo: 'More indigo',
pink: 'Pinker',
};
/**
* Max gray chroma (% of chroma of undertone) per hue
*/
export const maxGrayChroma = {
red: 0.2,
orange: 0.2,
yellow: 0.25,
green: 0.25,
cyan: 0.3,
blue: 0.35,
indigo: 0.35,
purple: 0.3,
pink: 0.25,
};
export const docsURLs = {
colors: '/docs/themes/',
palette: '/docs/palettes/',
typography: '/docs/themes/',
};
export const icons = {
colors: 'palette',
palette: 'swatchbook',
brand: 'droplet',
typography: 'font-case',
};
export const hues = Object.keys(hueRanges);
export const tints = ['05', '10', '20', '30', '40', '50', '60', '70', '80', '90', '95'];

View File

@@ -0,0 +1,81 @@
const IDENTITY = x => x;
export default class Permalink extends URLSearchParams {
/** Params changed since last URL I/O */
changed = false;
constructor(params) {
super(location.search);
this.params = params;
}
toJSON() {
return Object.fromEntries(this.entries());
}
#mappings = new WeakMap();
mapObject(obj, mapping = {}) {
this.#mappings.set(obj, mapping);
}
readFrom(obj) {
let mapping = this.#mappings.get(obj) ?? {};
let { keyFrom = IDENTITY, valueFrom = IDENTITY } = mapping;
for (let key in obj) {
let value = obj[key];
let mappedValue = valueFrom(value);
let mappedKey = keyFrom(key);
this.set(mappedKey, mappedValue);
}
}
writeTo(obj) {
let mapping = this.#mappings.get(obj) ?? {};
let { keyTo = IDENTITY, valueTo = IDENTITY, canExtend = false } = mapping;
for (let [key, value] of this) {
let mappedKey = keyTo(key);
let mappedValue = valueTo(value);
if (canExtend || mappedKey in obj) {
obj[mappedKey] = mappedValue;
}
}
}
set(key, value, defaultValue) {
let oldValue = this.get(key);
if (!value || value == defaultValue) {
super.delete(key);
if (oldValue) {
this.changed = true;
}
} else {
super.set(key, value);
if (String(value) !== String(oldValue)) {
this.changed = true;
}
}
this.sort();
}
/**
* Update page URL if it has changed since last time
*/
updateLocation() {
if (this.changed) {
// If theres already a search, replace it.
// We dont want to clog the users history while they iterate
let search = this.toString();
let historyAction = location.search && search ? 'replaceState' : 'pushState';
history[historyAction](null, '', `?${search}`);
this.changed = false;
}
}
}

View File

@@ -0,0 +1,36 @@
export function normalizeAngles(angles) {
// First, normalize
angles = angles.map(h => ((h % 360) + 360) % 360);
// Remove top and bottom 25% and find average
let averageHue =
angles
.toSorted((a, b) => a - b)
.slice(angles.length / 4, -angles.length / 4)
.reduce((a, b) => a + b, 0) / angles.length;
for (let i = 0; i < angles.length; i++) {
let h = angles[i];
let prevHue = angles[i - 1];
let delta = h - prevHue;
if (Math.abs(delta) > 180) {
let equivalent = [h + 360, h - 360];
// Offset hue to minimize difference in the direction that brings it closer to the average
let delta = h - averageHue;
if (Math.abs(equivalent[0] - prevHue) <= Math.abs(equivalent[1] - prevHue)) {
angles[i] = equivalent[0];
} else {
angles[i] = equivalent[1];
}
}
}
return angles;
}
export function subtractAngles(θ1, θ2) {
let [a, b] = normalizeAngles([θ1, θ2]);
return a - b;
}

View File

@@ -27,3 +27,19 @@ wa-copy-button.copy-button {
opacity: 1;
}
}
.block-link-icon {
position: absolute;
inset-block-start: 0;
inset-inline-end: calc(100% + var(--wa-space-s));
transition: var(--wa-transition-slow);
&:not(:hover, :focus) {
opacity: 50%;
}
:not(:hover, :focus-within) > & {
opacity: 0;
}
}

View File

@@ -156,7 +156,7 @@ wa-page > header {
}
/* Pro badges */
wa-badge.pro::part(base) {
wa-badge.pro {
background-color: var(--wa-brand-orange);
border-color: var(--wa-brand-orange);
}
@@ -188,6 +188,29 @@ wa-badge.pro::part(base) {
}
}
}
wa-icon-button.delete {
vertical-align: -0.2em;
margin-inline-start: var(--wa-space-xs);
&:not(li:hover > *, :focus) {
opacity: 0;
}
}
}
wa-icon-button.delete {
&:hover {
color: var(--wa-color-danger-on-quiet);
}
&::part(base):hover {
background: var(--wa-color-danger-fill-quiet);
}
&:not(:hover, :focus) {
opacity: 0.5;
}
}
#sidebar-close-button {
@@ -232,16 +255,32 @@ wa-page > main {
margin-inline: auto;
}
h1.title wa-badge {
vertical-align: middle;
h1.title {
wa-icon-button {
font-size: var(--wa-font-size-l);
color: var(--wa-color-text-quiet);
&::part(base) {
&:not(:hover, :focus) {
opacity: 0.5;
}
}
wa-badge {
vertical-align: middle;
font-size: 1.5rem;
}
}
.block-info {
display: flex;
gap: var(--wa-space-xs);
flex-wrap: wrap;
align-items: center;
margin-block-end: var(--wa-flow-spacing);
code {
line-height: var(--wa-line-height-condensed);
}
}
/* Current link */
@@ -360,7 +399,7 @@ wa-page > main:has(> .index-grid) {
}
&::part(header) {
background-color: var(--wa-color-neutral-fill-quiet);
background-color: var(--header-background, var(--wa-color-neutral-fill-quiet));
border-bottom: none;
display: flex;
align-items: center;
@@ -400,9 +439,18 @@ wa-page > main:has(> .index-grid) {
&.color {
border-color: transparent;
transition: background var(--wa-transition-slow);
background: linear-gradient(var(--color-2, transparent) 0% 100%) no-repeat border-box var(--color,);
background-position: var(--color-2-position, bottom);
background-size: var(--color-2-width, 100%) var(--color-2-height, 50%);
&.contrast-fail {
outline: 1px dashed var(--wa-color-red);
outline-offset: calc(-1 * var(--wa-space-2xs));
}
}
wa-copy-button {
> wa-copy-button {
position: absolute;
top: 0;
left: 0;
@@ -437,6 +485,7 @@ table.colors {
padding-block: 0;
}
}
tbody {
tr {
border: none;
@@ -456,6 +505,59 @@ table.colors {
padding-block: var(--wa-space-s);
}
}
.core-column {
padding-inline-end: var(--wa-space-xl);
}
}
.value-up,
.value-down {
position: relative;
&::after {
content: ' ' var(--icon);
position: absolute;
margin-inline-start: 3em;
scale: 1 0.6;
color: color-mix(in oklch, oklch(from var(--icon-color) none c h) 0%, oklch(from currentColor l none none));
font-size: 90%;
}
}
.value-down {
--icon: '▼';
--icon-color: var(--wa-color-danger-fill-quiet);
&::after {
margin-block-end: -0.2em;
}
}
.value-up {
--icon: '▲';
--icon-color: var(--wa-color-success-fill-quiet);
}
.icon-modifier {
position: relative;
display: inline-flex;
.modifier {
position: absolute;
bottom: -0.1em;
right: -0.3em;
font-size: 60%;
&::part(svg) {
stroke: var(--background-color, var(--wa-color-surface-default));
stroke-width: 100px;
paint-order: stroke;
overflow: visible;
stroke-linecap: round;
stroke-linejoin: round;
}
}
}
/* Layout Examples */
@@ -538,23 +640,4 @@ table.colors {
height: 65vh;
max-height: 21lh;
}
#mix_and_match {
strong {
display: flex;
align-items: center;
gap: var(--wa-space-2xs);
margin-top: 1.2em;
}
wa-select::part(label) {
margin-block-end: 0;
}
wa-select[value='']::part(display-input),
wa-option[value=''] {
font-style: italic;
color: var(--wa-color-text-quiet);
}
}
}

View File

@@ -0,0 +1,34 @@
.theme-color-icon {
display: grid;
gap: var(--wa-space-xs);
grid-template-columns: repeat(4, auto);
div {
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
border-radius: var(--wa-border-radius-m);
background-color: var(--background-color);
border: var(--wa-border-width-s) var(--wa-border-style) var(--border-color);
padding: var(--wa-space-2xs) var(--wa-space-xs);
color: var(--text-color);
font-weight: var(--wa-font-weight-semibold);
&.plain {
font-weight: var(--wa-font-weight-bold);
}
}
}
.theme-typography-icon {
display: flex;
flex-direction: column;
gap: var(--wa-space-xs);
h3,
p {
margin-block: 0;
padding: 0;
}
}

View File

@@ -9,6 +9,10 @@ icon: copy-button
<wa-copy-button value="Web Awesome rocks!"></wa-copy-button>
```
:::info
Copy buttons use the browser's [`clipboard.writeText()`](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/writeText) method, which requires a [secure context](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts) (HTTPS) in most browsers.
:::
## Examples
### Custom Labels

View File

@@ -52,8 +52,8 @@ wa-page {
}
&[slot^='navigation'] {
background: var(--wa-color-violet-80);
color: var(--wa-color-violet-20);
background: var(--wa-color-purple-80);
color: var(--wa-color-purple-20);
}
strong {

View File

@@ -74,3 +74,15 @@ Add the `size` attribute to the [Radio Group](/docs/components/radio-group) to c
<wa-radio value="3">Large 3</wa-radio>
</wa-radio-group>
```
### Hint
Add descriptive hint to a switch with the `hint` attribute. For hints that contain HTML, use the `hint` slot instead.
```html {.example}
<wa-radio-group label="Select an option" name="a" value="1">
<wa-radio value="1" hint="What should the user know about radio 1?">Option 1</wa-radio>
<wa-radio value="2" hint="What should the user know about radio 2?">Option 2</wa-radio>
<wa-radio value="3" hint="What should the user know about radio 3?">Option 3</wa-radio>
</wa-radio-group>
```

View File

@@ -130,6 +130,15 @@ Note that multi-select options may wrap, causing the control to expand verticall
Use the `value` attribute to set the initial selection.
```html {.example}
<wa-select value="option-1">
<wa-option value="option-1">Option 1</wa-option>
<wa-option value="option-2">Option 2</wa-option>
<wa-option value="option-3">Option 3</wa-option>
<wa-option value="option-4">Option 4</wa-option>
</wa-select>
```
When using `multiple`, the `value` _attribute_ uses space-delimited values to select more than one option. Because of this, `<wa-option>` values cannot contain spaces. If you're accessing the `value` _property_ through Javascript, it will be an array.
```html {.example}
@@ -294,7 +303,7 @@ Remember that custom tags are rendered in a shadow root. To style them, you can
return `
<wa-tag removable>
<wa-icon name="${name}" style="padding-inline-end: .5rem;"></wa-icon>
${option.getTextLabel()}
${option.label}
</wa-tag>
`;
};

View File

@@ -16,21 +16,21 @@ To use a theme, simply add a link to the theme's stylesheet to the `<head>` of y
<link rel="stylesheet" href="{% cdnUrl 'styles/themes/awesome.css' %}" />
```
You can [customize any theme](/docs/themes/creating) just with CSS — no preprocessor required. All design tokens are prefixed with `--wa-` to avoid collisions with other libraries or your own custom properties. Simply override any design token in your own stylesheet by scoping your styles to `:where(:root)`, `:host`, the class for the specific theme you want to override (if needed), and the class for the relevant color scheme (if needed). Here's an example that changes the default brand color to violet in light mode:
You can [customize any theme](/docs/themes/creating) just with CSS — no preprocessor required. All design tokens are prefixed with `--wa-` to avoid collisions with other libraries or your own custom properties. Simply override any design token in your own stylesheet by scoping your styles to `:where(:root)`, `:host`, the class for the specific theme you want to override (if needed), and the class for the relevant color scheme (if needed). Here's an example that changes the default brand color to purple in light mode:
```css
:where(:root),
:host,
.wa-light,
.wa-dark .wa-invert {
--wa-color-brand-fill-quiet: var(--wa-color-violet-95);
--wa-color-brand-fill-normal: var(--wa-color-violet-90);
--wa-color-brand-fill-loud: var(--wa-color-violet-50);
--wa-color-brand-border-quiet: var(--wa-color-violet-90);
--wa-color-brand-border-normal: var(--wa-color-violet-80);
--wa-color-brand-border-loud: var(--wa-color-violet-60);
--wa-color-brand-on-quiet: var(--wa-color-violet-40);
--wa-color-brand-on-normal: var(--wa-color-violet-30);
--wa-color-brand-fill-quiet: var(--wa-color-purple-95);
--wa-color-brand-fill-normal: var(--wa-color-purple-90);
--wa-color-brand-fill-loud: var(--wa-color-purple-50);
--wa-color-brand-border-quiet: var(--wa-color-purple-90);
--wa-color-brand-border-normal: var(--wa-color-purple-80);
--wa-color-brand-border-loud: var(--wa-color-purple-60);
--wa-color-brand-on-quiet: var(--wa-color-purple-40);
--wa-color-brand-on-normal: var(--wa-color-purple-30);
--wa-color-brand-on-loud: white;
}
```
@@ -99,8 +99,8 @@ Parts allow you to style *any* standard CSS property, not just those exposed thr
<style>
.gradient-button::part(base) {
background: linear-gradient(217deg, var(--wa-color-indigo-50), var(--wa-color-violet-50), var(--wa-color-red-50));
border: solid 1px var(--wa-color-violet-50);
background: linear-gradient(217deg, var(--wa-color-indigo-50), var(--wa-color-purple-50), var(--wa-color-red-50));
border: solid 1px var(--wa-color-purple-50);
transition: transform 100ms, box-shadow 100ms;
}

View File

@@ -0,0 +1,47 @@
---
title: Clamped brand tokens
layout: block
---
{% set tints = ['40-max', '50-max', '60-max', '40-min', '50-min', '60-min'] %}
{% for hue in hues %}
<link href="/dist/styles/brand/{{ hue }}.css" rel="stylesheet">
{% endfor %}
<table class="colors">
<thead>
<tr>
<th></th>
<th class="core-column">Core tint</th>
{% for tint in tints -%}
<th>{{ tint }}</th>
{%- endfor %}
</tr>
</thead>
{% for hue in hues -%}
<tr class="wa-brand-{{ hue }}">
<th>{{ hue | capitalize }}</th>
<td class="core-column">
<div class="color swatch" style="background-color: var(--wa-color-brand); color: var(--wa-color-brand-on);">
{{ palettes[paletteId][hue].maxChromaTint }}
<wa-copy-button value="--wa-color-brand" copy-label="--wa-color-brand"></wa-copy-button>
</div>
</td>
{% for tint in tints -%}
<td>
<div class="color swatch" style="background-color: var(--wa-color-brand-{{ tint }})">
<wa-copy-button value="--wa-color-brand-{{ tint }}" copy-label="--wa-color-brand-{{ tint }}"></wa-copy-button>
</div>
</td>
{%- endfor -%}
</tr>
{%- endfor %}
</table>
<style>
.core-column .color.swatch::before {
counter-reset: key var(--wa-color-brand-key);
content: counter(key);
}
</style>

View File

@@ -576,10 +576,10 @@ hasOutline: false
<wa-radio value="yellow"><span style="background-color:var(--wa-color-yellow-60);"></span></wa-radio>
<wa-radio value="lime"><span style="background-color:var(--wa-color-lime-60);"></span></wa-radio>
<wa-radio value="green"><span style="background-color:var(--wa-color-green-60);"></span></wa-radio>
<wa-radio value="teal"><span style="background-color:var(--wa-color-teal-60);"></span></wa-radio>
<wa-radio value="cyan"><span style="background-color:var(--wa-color-cyan-60);"></span></wa-radio>
<wa-radio value="blue"><span style="background-color:var(--wa-color-blue-60);"></span></wa-radio>
<wa-radio value="indigo"><span style="background-color:var(--wa-color-indigo-60);"></span></wa-radio>
<wa-radio value="violet"><span style="background-color:var(--wa-color-violet-60);"></span></wa-radio>
<wa-radio value="purple"><span style="background-color:var(--wa-color-purple-60);"></span></wa-radio>
<wa-radio value="gray"><span style="background-color:var(--wa-color-gray-60);"></span></wa-radio>
</wa-radio-group>
</wa-details>

View File

@@ -42,6 +42,14 @@ wa-code-demo::part(preview) {
<wa-input label="WA Input (url)" type="url"></wa-input>
```
## Pill shaped text fields
Add the `wa-pill` class to an `<input>` to make it pill-shaped.
```html {.example}
<label>Input <input type="text" placeholder="placeholder" class="wa-pill"></label>
```
## Color Picker
Basic:

View File

@@ -9,19 +9,11 @@ elements:
file: styles/native/radio.css
---
<style>
label {
display: inline-block;
}
label + label {
margin-inline-start: var(--wa-space);
}
</style>
```html {.example}
<label><input type="radio" name="radio" value="1"> Option 1</label>
<label><input type="radio" name="radio" value="2"> Option 2</label>
<div class="wa-cluster">
<label><input type="radio" name="radio" value="1"> Option 1</label>
<label><input type="radio" name="radio" value="2"> Option 2</label>
</div>
```
## Examples
@@ -31,9 +23,11 @@ file: styles/native/radio.css
To set the initial value and checked state, use the `checked` attribute on the corresponding radio.
```html {.example}
<label><input type="radio" name="radio" value="1"> Option 1</label>
<label><input type="radio" name="radio" value="2"> Option 2</label>
<label><input type="radio" name="radio" value="3" checked> Option 3</label>
<div class="wa-cluster">
<label><input type="radio" name="radio" value="1"> Option 1</label>
<label><input type="radio" name="radio" value="2"> Option 2</label>
<label><input type="radio" name="radio" value="3" checked> Option 3</label>
</div>
```
### Disabled
@@ -41,9 +35,11 @@ To set the initial value and checked state, use the `checked` attribute on the c
Use the `disabled` attribute to disable a radio.
```html {.example}
<label><input type="radio" name="radio" value="1"> Option 1</label>
<label><input type="radio" name="radio" value="2" disabled> Option 2</label>
<label><input type="radio" name="radio" value="3"> Option 3</label>
<div class="wa-cluster">
<label><input type="radio" name="radio" value="1"> Option 1</label>
<label><input type="radio" name="radio" value="2" disabled> Option 2</label>
<label><input type="radio" name="radio" value="3"> Option 3</label>
</div>
```
### Sizes
@@ -51,27 +47,47 @@ Use the `disabled` attribute to disable a radio.
Use the [size utilities](/docs/utilities/size) to change the radios' size.
```html {.example}
<fieldset class="wa-size-s">
<legend>Small</legend>
<label><input type="radio" name="radio" value="1"> Option 1</label>
<label><input type="radio" name="radio" value="2"> Option 2</label>
<label><input type="radio" name="radio" value="3"> Option 3</label>
<fieldset class="wa-size-s wa-cluster">
<legend>Small</legend>
<label><input type="radio" name="radio" value="1"> Option 1</label>
<label><input type="radio" name="radio" value="2"> Option 2</label>
<label><input type="radio" name="radio" value="3"> Option 3</label>
</fieldset>
<br />
<fieldset class="wa-size-m">
<legend>Medium</legend>
<label><input type="radio" name="radio" value="1"> Option 1</label>
<label><input type="radio" name="radio" value="2"> Option 2</label>
<label><input type="radio" name="radio" value="3"> Option 3</label>
<fieldset class="wa-size-m wa-cluster">
<legend>Medium</legend>
<label><input type="radio" name="radio" value="1"> Option 1</label>
<label><input type="radio" name="radio" value="2"> Option 2</label>
<label><input type="radio" name="radio" value="3"> Option 3</label>
</fieldset>
<br />
<fieldset class="wa-size-l">
<legend>Large</legend>
<label><input type="radio" name="radio" value="1"> Option 1</label>
<label><input type="radio" name="radio" value="2"> Option 2</label>
<label><input type="radio" name="radio" value="3"> Option 3</label>
<fieldset class="wa-size-l wa-cluster">
<legend>Large</legend>
<label><input type="radio" name="radio" value="1"> Option 1</label>
<label><input type="radio" name="radio" value="2"> Option 2</label>
<label><input type="radio" name="radio" value="3"> Option 3</label>
</fieldset>
```
### Orientation
You can wrap native radios in a flex container to give them a horizontal or vertical orientation with even spacing. The convenience [`wa-cluster`](/docs/utilities/cluster) and [`wa-stack`](/docs/utilities/stack) utilities make this easy.
```html {.example}
<div class="wa-cluster">
<label><input type="radio" name="radio" value="1"> Option 1</label>
<label><input type="radio" name="radio" value="2"> Option 2</label>
<label><input type="radio" name="radio" value="3" checked> Option 3</label>
</div>
```
```html {.example}
<div class="wa-stack">
<label><input type="radio" name="radio" value="1"> Option 1</label>
<label><input type="radio" name="radio" value="2"> Option 2</label>
<label><input type="radio" name="radio" value="3" checked> Option 3</label>
</div>
```

View File

@@ -0,0 +1,27 @@
---
layout: null
permalink: "/docs/palettes/data.json"
eleventyExcludeFromCollections: true
---
{
{% for palette in collections.palettes %}
{% set paletteId = palette.fileSlug -%}
{% set colors = palettes[paletteId] -%}
"{{ paletteId }}": {
"title": "{{ palette.data.title }}",
"colors": {
{% for hue, tints in colors -%}
"{{ hue }}": {
{% for tint, value in tints -%}
{% if tint != "05" -%}
{% set value = value.coords or value -%}
{% set key = "05" if tint == "5" else tint -%}
"{{ key }}": {{ value | dump | safe }}{{ ', ' if not loop.last }}
{%- endif %}
{% endfor -%}
}{{ ', ' if not loop.last }}
{% endfor %} {# end of hue #}
}
}{{ ', ' if not loop.last }} {# end of palette #}
{% endfor %}
}

View File

@@ -1,4 +1,5 @@
---
title: Default
description: This is the palette used in the default theme.
order: 0
---

View File

@@ -1,8 +1,10 @@
{
"layout": "palette.njk",
"tags": ["palettes", "palette"],
"wide": true,
"eleventyComputed": {
"snippet": ".wa-palette-{{ page.fileSlug }}",
"icon": "palette"
"icon": "palette",
"file": "styles/color/{{ page.fileSlug }}.css"
}
}

View File

@@ -0,0 +1,205 @@
:root {
--fa-sliders-simple: '\f1de';
}
.core-column {
position: relative;
> wa-dropdown {
min-width: 100%;
}
}
wa-dropdown > .color.swatch {
cursor: pointer;
}
.decorated-slider {
display: grid;
grid-template-columns: auto 1fr auto;
margin-block-end: var(--wa-space-xl);
wa-slider {
grid-column: 1 / -1;
--track-height: 1em;
--track-color-inactive: transparent;
--track-color-active: transparent;
--thumb-color: var(--color-tweaked, var(--color));
--thumb-shadow: 0 0 0 var(--thumb-gap) var(--wa-color-surface-default),
var(--wa-shadow-offset-x-m) var(--wa-shadow-offset-y-m) var(--wa-shadow-blur-m)
calc(var(--wa-shadow-offset-x-m) * -1 + var(--thumb-gap)) var(--wa-color-shadow);
&:active {
--thumb-size: 2em;
}
&::part(base) {
background: linear-gradient(to right in var(--color-interpolation-space, oklab), var(--color-1), var(--color-2));
}
}
[slot='label'] {
min-height: 1.1lh;
}
.clear-button {
vertical-align: middle;
font-size: var(--wa-font-size-xs);
&:not(.tweaked *) {
display: none;
}
}
.label-min,
.label-max {
font-size: var(--wa-font-size-xs);
}
.label-min {
grid-column: 1;
margin-inline-start: 0.15em;
}
.label-max {
grid-column: 3;
margin-inline-end: 0.1em;
}
}
.hue-shift-slider {
--color-1: oklch(from var(--color) l c calc(h + var(--min, 0)));
--color-2: oklch(from var(--color) l c calc(h + var(--max, 0)));
--color-interpolation-space: oklch;
}
.chroma-scale-slider {
--color: var(--wa-color-brand);
--color-1: oklch(from var(--color) l calc(c * var(--min)) h);
--color-2: oklch(from var(--color) l calc(c * var(--max)) h);
}
.gray-chroma-slider {
--color: var(--wa-color-gray);
--color-1: oklch(from var(--wa-color-gray) l 0 none);
--color-2: oklch(from var(--color-gray-undertone) l calc(c * var(--max)) h);
margin-top: var(--wa-space-m);
}
.popup {
background: var(--wa-color-surface-default);
border: 1px solid var(--wa-color-surface-border);
padding: var(--wa-space-xl);
border-radius: var(--wa-border-radius-m);
code {
white-space: nowrap;
}
}
.color-scale {
th {
white-space: nowrap;
}
td:not([data-hue='gray'] *) {
--tweak-c: calc(c * var(--chroma-scale, 1));
--tweak-h: calc(h + var(--hue-shift, 0));
--color-tweaked-no-chroma-scale: oklch(from var(--color) l c var(--tweak-h));
--color-tweaked-no-hue-shift: oklch(from var(--color) l var(--tweak-c) h);
&:is([data-tint='90'], [data-tint='95']) {
/* Work around https://bugs.webkit.org/show_bug.cgi?id=287637 */
--color-tweaked-no-chroma-scale: lch(from var(--color) l c var(--tweak-h));
--color-tweaked-no-hue-shift: lch(from var(--color) l var(--tweak-c) h);
/* outline: 1px dashed red; */
}
}
.color.swatch {
--color-2: var(--color-tweaked);
--color-2-height: 100%;
&:is(.tweaking *) {
--color-2-height: 70%;
}
&:is(.tweaking-chroma *) {
--color: var(--color-tweaked-no-chroma-scale);
}
&:is(.tweaking-hue *) {
--color: var(--color-tweaked-no-hue-shift);
}
&:is(.tweaking-gray-chroma *) {
--color: var(--color-tweaked-no-gray-chroma);
}
}
.tweak-icon {
position: absolute;
top: 50%;
transform: translateY(-50%);
right: var(--wa-space-s);
opacity: var(--tweak-icon-opacity, 0%);
}
.core-column:hover {
--tweak-icon-opacity: 40%;
}
&.tweaked .core-column {
--tweak-icon-opacity: 80%;
}
}
.tweaked-callout {
padding: var(--wa-space-xs);
padding-inline-start: var(--wa-space-m);
align-items: center;
&:not(.tweaked-any *) {
visibility: hidden;
}
&::part(message) {
display: flex;
align-items: center;
gap: var(--wa-space-xs);
}
wa-button:first-of-type {
margin-inline-start: auto;
}
}
/* Better UI before Vue initializes */
[v-if='saved'],
[v-if^='tweaked'] {
display: none;
}
.core-color {
wa-radio-button::part(base) {
width: 2em;
height: 2em;
padding: 0;
border-radius: var(--wa-border-radius-circle);
background: var(--color);
background-clip: border-box;
}
wa-radio-button:is([checked], :state(checked))::part(base) {
box-shadow:
inset 0 0 0 var(--indicator-width) var(--indicator-color),
inset 0 0 0 calc(var(--indicator-width) + 1.5px) var(--wa-color-surface-default);
}
&::part(form-control-input) {
gap: var(--wa-space-xs);
}
}

580
docs/docs/palettes/tweak.js Normal file
View File

@@ -0,0 +1,580 @@
// TODO move these to local imports
import Color from 'https://colorjs.io/dist/color.js';
import { createApp, nextTick } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js';
import { cdnUrl, hueRanges, hues, Permalink, tints } from '../../assets/scripts/tweak.js';
import { cssImport, cssLiteral, cssRule } from '../../assets/scripts/tweak/code.js';
import { maxGrayChroma, moreHue, selectors, urls } from '../../assets/scripts/tweak/data.js';
import { subtractAngles } from '../../assets/scripts/tweak/util.js';
import Prism from '/assets/scripts/prism.js';
await Promise.all(['wa-slider'].map(tag => customElements.whenDefined(tag)));
// // Detect https://bugs.webkit.org/show_bug.cgi?id=287637
// const SAFARI_OKLCH_BUG = (() => {
// let dummy = document.createElement('div');
// document.body.appendChild(dummy);
// dummy.style.color = 'oklch(from #d5e0e6 l c h)';
// let computedColor = getComputedStyle(dummy).color;
// dummy.remove();
// return computedColor.endsWith(' 0)');
// })();
let allPalettes = await fetch('/docs/palettes/data.json').then(r => r.json());
globalThis.allPalettes = allPalettes;
for (let palette in allPalettes) {
for (let hue in allPalettes[palette].colors) {
let scale = allPalettes[palette].colors[hue];
for (let tint of tints) {
let color = scale[tint];
if (Array.isArray(color)) {
scale[tint] = new Color('oklch', color);
}
}
}
}
const percentFormatter = value => value.toLocaleString(undefined, { style: 'percent' });
let paletteAppSpec = {
data() {
let appRoot = document.querySelector('#palette-app');
let paletteId = appRoot.dataset.paletteId;
let palette = allPalettes[paletteId];
return {
uid: undefined,
paletteId,
paletteTitle: palette.title,
originalColors: palette.colors,
permalink: new Permalink(),
hueRanges,
hueShifts: Object.fromEntries(hues.map(hue => [hue, 0])),
chromaScale: 1,
grayChroma: undefined,
grayColor: undefined,
tweaking: {},
saved: null,
};
},
created() {
// Non-reactive variables to expose
Object.assign(this, { moreHue });
// Read URL params and apply them. This facilitates permalinks.
this.permalink.mapObject(this.hueShifts, {
keyTo: key => key.replace(/-shift$/, ''),
keyFrom: key => key + '-shift',
valueFrom: value => (!value ? '' : Number(value)),
valueTo: value => (!value ? 0 : Number(value)),
});
this.grayChroma = this.originalGrayChroma;
this.grayColor = this.originalGrayColor;
if (location.search) {
// Update from URL
this.permalink.writeTo(this.hueShifts);
for (let param of ['chroma-scale', 'gray-color', 'gray-chroma']) {
if (this.permalink.has(param)) {
let value = this.permalink.get(param);
if (!isNaN(value)) {
// Convert numeric values to numbers
value = Number(value);
}
let prop = camelCase(param);
this[prop] = value;
}
}
if (this.permalink.has('uid')) {
this.uid = Number(this.permalink.get('uid'));
}
this.saved = sidebar.palette.getSaved(this.getPalette());
}
},
mounted() {
for (let ref in this.$refs) {
this.$refs[ref].tooltipFormatter = percentFormatter;
}
},
computed: {
tweaks() {
return {
hueShifts: this.hueShifts,
chromaScale: this.chromaScale,
grayColor: this.grayColor,
grayChroma: this.grayChroma,
};
},
isTweaked() {
return Object.values(this.hueShifts).some(Boolean);
},
code() {
let ret = {};
for (let language of ['html', 'css']) {
let code = getPaletteCode(this.paletteId, this.colors, this.tweaked, { language, cdnUrl });
ret[language] = {
raw: code,
highlighted: Prism.highlight(code, Prism.languages[language], language),
};
}
return ret;
},
colors() {
return applyTweaks.call(this, this.originalColors, this.tweaks, this.tweaked);
},
colorsMinusChromaScale() {
let tweaked = { ...this.tweaked, chromaScale: false };
return applyTweaks.call(this, this.originalColors, this.tweaks, tweaked);
},
colorsMinusHueShifts() {
let tweaked = { ...this.tweaked, hue: false };
return applyTweaks.call(this, this.originalColors, this.tweaks, tweaked);
},
colorsMinusGrayChroma() {
let tweaked = { ...this.tweaked, grayChroma: false };
return applyTweaks.call(this, this.originalColors, this.tweaks, tweaked);
},
tweaked() {
let anyHueTweaked = Object.values(this.hueShifts).some(Boolean);
let hue = anyHueTweaked
? Object.fromEntries(Object.entries(this.hueShifts).map(([hue, shift]) => [hue, shift !== 0]))
: false;
let ret = {
chromaScale: this.chromaScale !== 1,
hue,
grayChroma: this.grayChroma !== this.originalGrayChroma,
grayColor: this.grayColor !== this.originalGrayColor,
};
let anyTweaked = Object.values(ret).some(Boolean);
return anyTweaked ? ret : false;
},
tweaksHumanReadable() {
let ret = {};
if (this.chromaScale !== 1) {
ret.chromaScale = 'More ' + (this.chromaScale > 1 ? 'vibrant' : 'muted');
}
for (let hue in this.hueShifts) {
let shift = this.hueShifts[hue];
if (!shift) {
continue;
}
let relHue = shift < 0 ? arrayPrevious(hues, hue) : arrayNext(hues, hue);
let hueTweak = moreHue[relHue] ?? relHue + 'er';
ret[hue] = capitalize(hueTweak + ' ' + hue + 's');
}
if (this.tweaked.grayChroma || this.tweaked.grayColor) {
if (this.tweaked.grayChroma === 0) {
ret.grayChroma = 'Achromatic grays';
} else {
if (this.tweaked.grayColor) {
ret.grayColor = capitalize(this.grayColor) + ' gray undertone';
}
if (this.tweaked.grayChroma) {
let more = this.tweaked.grayChroma > this.originalGrayChroma;
ret.grayChroma = `More ${more ? 'colorful' : 'neutral'} grays`;
}
}
}
return ret;
},
originalContrasts() {
return getContrasts(this.originalColors);
},
contrasts() {
return getContrasts(this.colors, this.originalContrasts);
},
originalCoreColors() {
let ret = {};
for (let hue in this.originalColors) {
let maxChromaTintRaw = this.originalColors[hue].maxChromaTintRaw;
ret[hue] = this.originalColors[hue][maxChromaTintRaw];
}
return ret;
},
coreColors() {
let ret = {};
for (let hue in this.colors) {
let maxChromaTintRaw = this.colors[hue].maxChromaTintRaw;
ret[hue] = this.colors[hue][maxChromaTintRaw];
}
return ret;
},
originalGrayColor() {
let grayHue = this.originalCoreColors.gray.get('h');
let minDistance = Infinity;
let closestHue = null;
for (let name in this.originalCoreColors) {
if (name === 'gray') {
continue;
}
let hue = this.originalCoreColors[name].get('h');
let distance = Math.abs(subtractAngles(hue, grayHue));
if (distance < minDistance) {
minDistance = distance;
closestHue = name;
}
}
return closestHue ?? 'indigo';
},
originalGrayChroma() {
let coreTint = this.originalColors.gray.maxChromaTint;
let grayChroma = this.originalColors.gray[coreTint].get('c');
if (grayChroma === 0 || grayChroma === null) {
return 0;
}
let grayColorChroma = this.originalColors[this.originalGrayColor][coreTint].get('c');
return grayChroma / grayColorChroma;
},
/**
* We want to preserve the original grayChroma selection so that when the user switches to another undertone
* that supports higher chromas, their selection will be there.
* This property is the gray chroma % that is actually applied.
*/
computedGrayChroma() {
return Math.min(this.grayChroma, this.maxGrayChroma);
},
maxGrayChroma() {
return maxGrayChroma[this.grayColor] ?? 0.3;
},
},
watch: {
hueShifts: {
deep: true,
handler() {
this.permalink.readFrom(this.hueShifts);
},
},
chromaScale() {
this.permalink.set('chroma-scale', this.chromaScale, 1);
},
grayColor() {
this.permalink.set('gray-color', this.grayColor, this.originalGrayColor);
},
grayChroma() {
this.permalink.set('gray-chroma', this.grayChroma, this.originalGrayChroma);
},
tweaks: {
deep: true,
async handler(value, oldValue) {
await nextTick(); // must run after individual watchers
// Update page URL
this.permalink.updateLocation();
if (this.saved) {
this.save({ silent: true });
}
},
},
},
methods: {
getPalette() {
return { id: this.paletteId, uid: this.uid, search: location.search };
},
save({ silent } = {}) {
let title = silent
? (this.saved?.title ?? this.paletteTitle)
: prompt('Palette title:', `${this.paletteTitle} (tweaked)`);
if (!title) {
return;
}
let uid = this.uid;
if (!uid) {
// First time saving
this.uid = uid = sidebar.palette.getUid();
this.permalink.set('uid', uid);
this.permalink.updateLocation();
}
let palette = { ...this.getPalette(), uid, title };
sidebar.palette.save(palette, this.saved);
this.saved = palette;
},
rename() {
if (!this.saved) {
return;
}
let newTitle = prompt('New title:', this.saved.title);
if (!newTitle) {
return;
}
this.saved.title = newTitle;
sidebar.palette.save(this.saved);
},
deleteSaved() {
sidebar.palette.delete(this.saved);
},
postDelete() {
this.saved = null;
this.permalink.delete('uid');
this.uid = undefined;
this.permalink.updateLocation();
},
/**
* Remove a specific tweak or all tweaks
* @param {string} [param] - The tweak to remove. If not provided, all tweaks are removed.
*/
reset(param) {
if (!param || param === 'chromaScale') {
this.chromaScale = 1;
}
if (param in this.hueShifts) {
this.hueShifts[param] = 0;
} else if (!param) {
for (let hue in this.hueShifts) {
this.hueShifts[hue] = 0;
}
}
if (!param || param === 'grayColor') {
this.grayColor = this.originalGrayColor;
}
if (!param || param === 'grayChroma') {
this.grayChroma = this.originalGrayChroma;
}
},
},
directives: {
// Like v-text, but doesn't complain if the element has content,
// making it possible to use in a PE fashion, with the contents being the fallback
content(el, { value, arg }) {
if (!el.dataset.fallback) {
// Store the original content as a fallback the first time
el.dataset.fallback = el.textContent;
}
if (value === '') {
value = el.dataset.fallback;
} else {
if (arg === 'number') {
value = Number(value).toLocaleString(undefined, { maximumSignificantDigits: 2 });
}
}
if (arg === 'html') {
el.innerHTML = value;
} else {
el.textContent = value;
}
},
},
compilerOptions: {
isCustomElement: tag => tag.startsWith('wa-'),
},
};
function init() {
let paletteAppContainer = document.querySelector('#palette-app');
globalThis.paletteApp?.unmount?.();
if (!paletteAppContainer) {
return;
}
globalThis.paletteApp = createApp(paletteAppSpec).mount(paletteAppContainer);
}
init();
addEventListener('turbo:render', init);
export function getPaletteCode(paletteId, colors, tweaked, options) {
let imports = [];
if (paletteId) {
imports.push(urls.palette(paletteId));
}
let css = '';
let declarations = [];
if (tweaked) {
for (let hue in colors) {
if (hue === 'orange') {
continue;
} else if (hue === 'gray') {
if (!tweaked.grayChroma && !tweaked.grayColor) {
continue;
}
} else if (!tweaked.chromaScale && !tweaked.hue?.[hue]) {
continue;
}
for (let tint of tints) {
let color = colors[hue][tint];
let stringified = color.toString({ format: color.inGamut('srgb') ? 'hex' : undefined });
declarations.push(`--wa-color-${hue}-${tint}: ${stringified};`);
}
declarations.push('');
}
if (declarations.length > 0) {
css += cssRule(selectors.palette(paletteId), declarations);
}
}
let ret = imports.map(url => cssImport(url, options)).join('\n');
if (css) {
ret += `\n\n${cssLiteral(css, options)}`;
}
return ret;
}
function arrayNext(array, element) {
let index = array.indexOf(element);
return array[(index + 1) % array.length];
}
function arrayPrevious(array, element) {
let index = array.indexOf(element);
return array[(index - 1 + array.length) % array.length];
}
function applyTweaks(originalColors, tweaks, tweaked) {
let ret = {};
let { hueShifts, chromaScale = 1, grayColor, grayChroma } = tweaks;
if (!tweaked) {
return originalColors;
}
if (tweaked.grayChroma) {
grayChroma = this.computedGrayChroma;
}
for (let hue in originalColors) {
let originalScale = originalColors[hue];
let scale = (ret[hue] = {});
let descriptors = Object.getOwnPropertyDescriptors(originalScale);
Object.defineProperties(scale, {
maxChromaTint: { ...descriptors.maxChromaTint, enumerable: false },
maxChromaTintRaw: { ...descriptors.maxChromaTintRaw, enumerable: false },
});
for (let tint of tints) {
let color = originalScale[tint].clone();
if (tweaked.hue && hueShifts[hue]) {
color.set({ h: h => h + hueShifts[hue] });
}
if (tweaked.chromaScale && chromaScale !== 1) {
color.set({ c: c => c * chromaScale });
}
if (hue === 'gray' && (tweaked.grayChroma || tweaked.grayColor)) {
let colorUndertone = originalColors[grayColor][tint].clone();
color = colorUndertone.set({ c: c => c * grayChroma });
}
scale[tint] = color;
}
}
return ret;
}
function camelCase(str) {
return (str + '').replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
}
function capitalize(str) {
return str[0].toUpperCase() + str.slice(1);
}
function getContrasts(colors, originalContrasts) {
let ret = {};
for (let hue in colors) {
ret[hue] = {};
for (let tintBg of tints) {
ret[hue][tintBg] = {};
let bgColor = colors[hue][tintBg];
if (!bgColor || !bgColor.contrast) {
continue;
}
for (let tintFg of tints) {
let fgColor = colors[hue][tintFg];
let value = bgColor.contrast(fgColor, 'WCAG21');
if (originalContrasts) {
let original = originalContrasts[hue][tintBg][tintFg];
ret[hue][tintBg][tintFg] = { value, original, bgColor, fgColor };
} else {
ret[hue][tintBg][tintFg] = value;
}
}
}
}
return ret;
}

View File

@@ -1,7 +1,7 @@
---
title: Changelog
description: Changes to each version of the project are documented here.
layout: page
layout: page-outline
---
Web Awesome follows [Semantic Versioning](https://semver.org/). Breaking changes in components with the <wa-badge variant="brand" pill>Stable</wa-badge> badge will not be accepted until the next major version. As such, all contributions must consider the project's roadmap and take this into consideration. Features that are deemed no longer necessary will be deprecated but not removed.
@@ -14,6 +14,71 @@ During the alpha period, things might break! We take breaking changes very serio
## Next
- Fixed `wa-pill` class for text fields
- Fixed `pill` style for `<wa-input>` elements
## 3.0.0-alpha.11
### Color Palettes
- Color palette tweaking UI. Tweak hue, grays, overall colorfulness, save or share the results.
- Added a `pink` scale to all color palettes
- Tweaked hues of all color palettes to make them more distinct and make their hues more intentional
- Dropped `violet` and `teal`, instead using `purple` and `cyan` (this is not just a renaming, the colors have been adjusted too).
### Design Tokens
- Added `--wa-color-[hue]` tokens with the "core" color of each scale, regardless of which tint it lives on.
You can find them in the first column of each color palette.
### Themes
- Improved UI for theme remixing:
- You can now override the brand color of any theme with any of the 9 hues supported.
- Rich previews
- Generated copyable code snippets.
- Permalinks
- Updated Active, Glossy, Playful, and Premium themes so that `--wa-color-brand-fill-loud` uses the core color of the chosen brand color, regardless of tint.
### Components
#### `<wa-radio>`
- Dropped the `base` part. It can now be styled by directly applying CSS to the element itself.
- Added `hint` attribute and corresponding slot.
#### `<wa-select>`
- Added the `tag` part (and associated exported parts) to `<wa-select>` to allow targeting the tag that shows when more than the max number of visible items have been selected
- Fixed a bug that prevented the placeholder color from being customized with the `--wa-form-control-placeholder-color` token
- Fixed an incorrect CSS value in the expand icon
- Fixed a bug that prevented the description from being read by screen readers
#### `<wa-option>`
- `label` attribute to override the generated label (useful for rich content)
- `defaultLabel` property
- Dropped `getTextLabel()` method (if you need dynamic labels, just set the `label` attribute dynamically)
- Dropped `base` part for easier styling. CSS can now be applied directly to the element itself.
#### `<wa-menu-item>`
- `label` attribute to override the generated label (useful for rich content)
- `defaultLabel` property
- Dropped `getTextLabel()` method (if you need dynamic labels, just set the `label` attribute dynamically)
#### `<wa-card>`
- Fixed a bug where child elements did not have correct rounding when headers and footers were absent.
- Re-introduced `--border-color` so that the card itself can have a different border color than its inner borders.
- Fixed a bug that prevented slots from showing automatically without `with-` attributes
#### `<wa-tab>`
- Fixed a bug that caused `document.createElement('wa-tab')` to fail (which also meant it could not be used in VueJS and other frameworks)
### Docs
- Added an orientation example to the native radio docs
- Fixed a number of broken event listeners throughout the docs
## 3.0.0-alpha.10

View File

@@ -4,4 +4,5 @@ description: Energetic and tactile, always in motion.
isPro: true
tags: pro
palette: rudimentary
brand: green
---

View File

@@ -3,4 +3,5 @@ title: Awesome
description: Punchy and vibrant, the rockstar of themes.
order: 0.2
palette: bright
brand: blue
---

View File

@@ -4,4 +4,5 @@ description: Sharp, square, and unapologetically bold.
isPro: true
tags: pro
palette: default
brand: blue
---

View File

@@ -3,4 +3,5 @@ title: Default
description: Your trusty companion, like a perfectly broken-in pair of jeans.
order: 0
palette: default
brand: blue
---

View File

@@ -10,16 +10,17 @@ override:tags: []
eleventyComputed:
forceTheme: "{{ theme.fileSlug }}"
---
<link rel="stylesheet" href="/dist/styles/themes/{{ theme.fileSlug }}.css" />
{% set isPro = theme.data.isPro %}
{% set status = theme.data.status %}
{% set since = theme.data.since %}
<link rel="stylesheet" href="/docs/themes/showcase.css" />
{% set content %}
<header>
{% include 'breadcrumbs.njk' %}
<h1 class="title">{{ theme.data.title }}</h1>
<p id="mix_and_match" hidden class="wa-size-s"></p>
<p>{% include 'status.njk' %}</p>
<p id="mix_and_match" class="wa-size-s"></p>
<p id="theme-status">{% include 'status.njk' %}</p>
<p id="theme-showcase-description">{{ theme.data.description | inlineMarkdown | safe }}</p>
</header>
{% include 'theme-showcase.njk' %}
@@ -34,46 +35,56 @@ eleventyComputed:
</div>
</wa-image-comparer>
<script>
<script type="module">
import { urls as stylesheetURLs, docsURLs, icons } from "/assets/scripts/tweak/data.js";
import { getThemeCode } from "/assets/scripts/tweak/code.js";
function updateTheme() {
let params = new URLSearchParams(window.location.search);
let script = document.currentScript;
const stylesheetURLs = {
colors: id => `/dist/styles/themes/${ id }/color.css`,
palette: id => `/dist/styles/color/${ id }.css`,
typography: id => `/dist/styles/themes/${ id }/typography.css`
};
const icons = {
colors: 'palette',
palette: 'swatchbook',
typography: 'font-case'
}
params = Object.fromEntries(params.entries());
for (let link of document.querySelectorAll('link.mix-and-match')) {
link.remove();
}
let msgs = [];
let tweaks = [];
let code = getThemeCode("{{ theme.fileSlug }}", params, {attributes: 'class="mix-and-match"'});
document.head.insertAdjacentHTML('beforeend', code);
for (let name in stylesheetURLs) {
if (params.get(name)) {
let url = stylesheetURLs[name](params.get(name));
script.insertAdjacentHTML('afterend', `<link rel="stylesheet" href="${ url }" class="mix-and-match" />`);
let docsURL = (name === 'palette' ? '/docs/palettes/' : '/docs/themes/') + params.get(name) + '/';
let title = params.get(name).replace(/^[a-z]/g, c => c.toUpperCase());
let override = params[name];
if (override) {
let docsURL = docsURLs[name] ? docsURLs[name] + override + '/' : '';
let title = override.replace(/^[a-z]/g, c => c.toUpperCase());
if (docsURL) {
title = `<a href="${ docsURL }">${ title }</a>`;
}
let icon = icons[name];
msgs.push(`<wa-icon name="${icon}" variant="regular"></wa-icon> <a href="${ docsURL }">${ title }</a>`);
tweaks.push(`<wa-icon name="${icon}" variant="regular"></wa-icon> ${ title }`);
}
}
for (let p of mix_and_match) {
p.hidden = msgs.length === 0;
if (msgs.length) {
let icon =
p.innerHTML = `<strong><wa-icon name="merge"></wa-icon> Remixed</strong> ` + msgs.map(msg => `<wa-badge appearance=outlined>
${ msg }</wa-badge>`).join(' ');
let isRemixed = tweaks.length > 0;
document.documentElement.classList.toggle('is-remixed', isRemixed);
if (isRemixed) {
for (let p of document.querySelectorAll("#theme-status")) {
let proBadge = p.querySelector(".pro");
if (!proBadge) {
p.insertAdjacentHTML('beforeend', '<wa-badge class="pro">PRO</wa-badge>');
}
}
for (let p of mix_and_match) {
if (tweaks.length) {
p.innerHTML = `<strong><wa-icon name="arrows-rotate"></wa-icon> Remixed</strong> ` + tweaks.map(msg => `<wa-badge appearance=outlined>
${ msg }</wa-badge>`).join(' ');
}
}
}
}
updateTheme();
</script>

View File

@@ -4,4 +4,5 @@ description: Bustling with plenty of luster and shine.
isPro: true
tags: pro
palette: elegant
brand: indigo
---

View File

@@ -4,6 +4,7 @@ description: Digital design inspired by the real world.
isPro: true
tags: pro
palette: mild
brand: indigo
---
Set the page theme to "{{ title }}" from the top right to preview the following examples.

View File

@@ -4,4 +4,5 @@ description: Cheerful and engaging, like a playground on screen.
isPro: true
tags: pro
palette: rudimentary
brand: purple
---

View File

@@ -4,4 +4,5 @@ description: The ultimate in sophistication and style.
isPro: true
tags: pro
palette: anodized
brand: cyan
---

127
docs/docs/themes/remix.css vendored Normal file
View File

@@ -0,0 +1,127 @@
#mix_and_match {
margin-block-end: var(--wa-space-2xl);
&::part(content) {
display: flex;
gap: var(--wa-space-xl);
padding-block-start: var(--wa-space-m);
}
> [slot='summary'] {
margin: 0;
wa-icon:first-of-type {
vertical-align: -0.15em;
color: var(--wa-color-text-quiet);
}
wa-icon#what-is-remixing {
vertical-align: -0.1em;
font-size: var(--wa-font-size-s);
color: var(--wa-color-text-quiet);
}
}
wa-select {
&::part(label) {
margin-block-end: 0;
}
}
wa-select:has(> wa-option > wa-card) {
&::part(listbox) {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
width: min(90vw, 800px);
}
&:state(blank)::part(display-input) {
font-style: italic;
color: var(--wa-color-text-quiet);
}
}
wa-option:has(> wa-card) {
position: relative;
padding: var(--wa-space-smaller);
&::part(checked-icon) {
position: absolute;
inset-block-start: calc(var(--wa-space-smaller) - 0.5em);
inset-inline-end: calc(var(--wa-space-smaller) - 0.5em);
width: 1em;
height: 1em;
line-height: 1em;
padding: 0.4em;
border-radius: var(--wa-border-radius-circle);
text-align: center;
background: var(--wa-color-brand-fill-loud);
color: var(--wa-color-brand-on-loud);
font-size: var(--wa-font-size-xs);
}
wa-card {
--spacing: var(--wa-space-smaller);
&::part(body) {
background: var(--wa-color-neutral-fill-quiet);
padding-block: var(--wa-space-s);
}
}
&:state(current),
&:state(hover),
&:state(selected),
&:hover {
background: transparent;
}
&:hover {
wa-card {
border-color: var(--wa-color-brand-border-loud);
box-shadow: 0 0 0 var(--wa-border-width-m) var(--wa-color-brand-border-normal);
}
}
&[aria-selected='true'] {
wa-card {
--border-color: var(--wa-color-brand-border-loud);
box-shadow: 0 0 0 var(--wa-border-width-m) var(--wa-color-brand-border-loud);
&::part(body) {
background: var(--wa-color-brand-fill-quiet);
}
}
}
}
.selected-swatch,
wa-select[name='brand'] wa-option::before {
content: '';
display: inline-block;
width: 1.2em;
aspect-ratio: 1;
flex: none;
border-radius: var(--wa-border-radius-m);
background: var(--color);
border: 1px solid var(--wa-color-surface-default);
}
wa-select[name='brand'] wa-option {
white-space: nowrap;
&::before {
width: 1em;
margin-inline: var(--wa-space-xs);
}
&::part(checked-icon) {
order: 2;
}
}
}
#test_select wa-option:state(selected) {
outline: 2px solid red;
background: yellow;
}

154
docs/docs/themes/remix.js vendored Normal file
View File

@@ -0,0 +1,154 @@
import Prism from '/assets/scripts/prism.js';
import { cdnUrl, getThemeCode, Permalink } from '/assets/scripts/tweak.js';
await Promise.all(['wa-select', 'wa-option', 'wa-details'].map(tag => customElements.whenDefined(tag)));
const domChange = document.startViewTransition ? document.startViewTransition.bind(document) : fn => fn();
let selects, data, codeSnippets;
let computed = {
get isRemixed() {
return Object.values(data.params).filter(Boolean).length > 0;
},
get palette() {
return data.params.palette || data.defaultParams.palette;
},
get brand() {
return data.params.brand || data.defaultParams.brand;
},
};
function selectsChanged(event) {
data.params[event.target.name] = event.target.value;
render(event.target.name);
}
function init() {
selects = Object.fromEntries(
[...document.querySelectorAll('#mix_and_match wa-select')].map(select => [select.getAttribute('name'), select]),
);
codeSnippets = document.querySelector('#usage ~ wa-tab-group.import-stylesheet-code:first-of-type');
codeSnippets = {
html: codeSnippets.querySelector('code.language-html'),
css: codeSnippets.querySelector('code.language-css'),
};
data = {
baseTheme: wa_data.baseTheme,
themes: wa_data.themes,
palettes: wa_data.palettes,
defaultParams: {
colors: '',
get palette() {
let colors = data.params.colors || data.baseTheme;
return data.themes[colors].palette;
},
get brand() {
let colors = data.params.colors || data.baseTheme;
return data.themes[colors].brand;
},
typography: '',
},
params: { colors: '', palette: '', brand: '', typography: '' },
urlParams: new Permalink(),
};
data.urlParams.mapObject(data.params);
data.urlParams.writeTo(data.params);
if (computed.isRemixed) {
// Start with the remixing UI open if the theme has been remixed
mix_and_match.setAttribute('open', '');
mix_and_match.open = true;
}
for (let name in selects) {
selects[name].addEventListener('change', selectsChanged);
}
Promise.all(Object.values(selects).map(select => select.updateComplete)).then(() => render());
return { selects, codeSnippets, data, computed, render };
}
globalThis.remixApp = init();
// Async load CSS for other themes *before* current theme stylesheet
let themeStylesheet = document.querySelector('#theme-stylesheet');
for (const theme in data.themes) {
themeStylesheet.insertAdjacentHTML(
'beforebegin',
`<link rel="preload" as="style" href="/dist/styles/themes/${theme}/color.css" onload="this.rel = 'stylesheet'" />
<link rel="preload" as="style" href="/dist/styles/themes/${theme}/typography.css" onload="this.rel = 'stylesheet'" />`,
);
}
for (const palette in data.palettes) {
themeStylesheet.insertAdjacentHTML(
'beforebegin',
`<link rel="preload" as="style" href="/dist/styles/color/${palette}.css" onload="this.rel = 'stylesheet'" />`,
);
}
function setDefault(select, value) {
let oldDefaultOption = select.querySelector(`wa-option[value=""]:not([data-id="${value}"])`);
let newDefaultOption = select.querySelector(`wa-option[value="${value}"]`);
if (oldDefaultOption) {
oldDefaultOption.value = oldDefaultOption.dataset.id;
}
if (newDefaultOption) {
newDefaultOption.dataset.id ??= newDefaultOption.value;
newDefaultOption.value = '';
}
}
function render(changedAspect) {
if (!globalThis.demo) {
return;
}
let url = new URL(demo.src);
if (!changedAspect || changedAspect === 'colors') {
// Update the default palette when the theme colors change to the default palette of that theme
setDefault(selects.palette, data.defaultParams.palette);
setDefault(selects.brand, data.defaultParams.brand);
}
let brand = data.params.brand || data.defaultParams.brand;
selects.brand.style.setProperty('--color', `var(--wa-color-${brand})`);
selects.brand.className = `wa-palette-${computed.palette}`;
for (let aspect in data.params) {
let value = data.params[aspect];
selects[aspect].value = value;
}
data.urlParams.readFrom(data.params);
// Update demo URL
domChange(() => {
url.search = data.urlParams;
demo.src = url;
return new Promise(resolve => (demo.onload = resolve));
});
// Update page URL
data.urlParams.updateLocation();
// Update code snippets
for (let language in codeSnippets) {
let codeSnippet = codeSnippets[language];
let code = getThemeCode(data.baseTheme, data.params, { language, cdnUrl });
codeSnippet.textContent = code;
Prism.highlightElement(codeSnippet);
}
}
addEventListener('turbo:render', event => {
remixApp = init();
});

View File

@@ -12,9 +12,25 @@ body,
#mix_and_match {
font-weight: var(--wa-font-weight-semibold);
color: var(--wa-color-text-quiet);
margin-block-end: var(--wa-space-xs);
html:not(.is-remixed) {
display: none;
}
wa-icon {
vertical-align: middle;
vertical-align: -0.15em;
}
> strong {
margin-inline-end: var(--wa-space-2xs);
}
wa-badge {
> a {
text-decoration: none;
color: inherit;
}
}
}

View File

@@ -4,4 +4,5 @@ description: Like a bird in flight, guiding you from there to here.
isPro: true
tags: pro
palette: vogue
brand: indigo
---

View File

@@ -1,5 +1,9 @@
{
"layout": "theme.njk",
"wide": true,
"tags": ["themes", "theme"]
"tags": ["themes", "theme"],
"brand": "blue",
"eleventyComputed": {
"file": "styles/themes/{{ page.fileSlug }}.css"
}
}

View File

@@ -66,7 +66,7 @@ You can use these numbers to ensure accessible color contrast per [WCAG 2.1 succ
Each Web Awesome palette defines seven literal colors each with 11 lightness values using the format `--wa-color-{hue}-{tint}`.
{% for hue in ["red", "yellow", "green", "teal", "blue", "indigo", "violet", "gray"] -%}
{% for hue in hues -%}
<div class="color-name">{{ hue | capitalize }}</div>
<ul class="color-group">
{% for tint in ["95", "90", "80", "70", "60", "50", "40", "30", "20", "10", "05"] -%}

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@shoelace-style/webawesome",
"version": "3.0.0-alpha.10",
"version": "3.0.0-alpha.11",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@shoelace-style/webawesome",
"version": "3.0.0-alpha.10",
"version": "3.0.0-alpha.11",
"license": "MIT",
"dependencies": {
"@ctrl/tinycolor": "^4.1.0",

View File

@@ -1,7 +1,7 @@
{
"name": "@shoelace-style/webawesome",
"description": "A forward-thinking library of web components.",
"version": "3.0.0-alpha.10",
"version": "3.0.0-alpha.11",
"homepage": "https://webawesome.com/",
"author": "Web Awesome",
"license": "MIT",

View File

@@ -52,6 +52,7 @@ import styles from './button.css';
@customElement('wa-button')
export default class WaButton extends WebAwesomeFormAssociatedElement {
static shadowStyle = [variantStyles, appearanceStyles, sizeStyles, nativeStyles, styles];
static rectProxy = 'button';
static get validators() {
return [...super.validators, MirrorValidator()];
@@ -224,17 +225,6 @@ export default class WaButton extends WebAwesomeFormAssociatedElement {
this.button.blur();
}
getBoundingClientRect(): DOMRect {
let rect = super.getBoundingClientRect();
let buttonRect = this.button.getBoundingClientRect();
if (rect.width === 0 && buttonRect.width > 0) {
return buttonRect;
}
return rect;
}
render() {
const isLink = this.isLink();
const tag = isLink ? literal`a` : literal`button`;

View File

@@ -5,6 +5,7 @@
--spacing: var(--wa-space);
--border-width: var(--wa-panel-border-width);
--border-color: var(--wa-color-surface-border);
--border-radius: var(--wa-panel-border-radius);
--inner-border-radius: calc(var(--border-radius) - var(--border-width));
@@ -12,7 +13,7 @@
display: flex;
flex-direction: column;
background-color: var(--wa-color-surface-default);
border-color: var(--wa-color-surface-border);
border-color: var(--border-color);
border-radius: var(--border-radius);
border-style: var(--wa-panel-border-style);
box-shadow: var(--wa-shadow-s);
@@ -20,12 +21,20 @@
color: var(--wa-color-text-normal);
}
/* Take care of top and bottom radii */
.image,
:host(:not([with-image])) .header {
:host(:not([with-image])) .header,
:host(:not([with-image], [with-header])) .body {
border-start-start-radius: var(--inner-border-radius);
border-start-end-radius: var(--inner-border-radius);
}
:host(:not([with-footer])) .body,
.footer {
border-end-start-radius: var(--inner-border-radius);
border-end-end-radius: var(--inner-border-radius);
}
.image {
display: flex;
@@ -39,7 +48,9 @@
.header {
display: block;
border-bottom: inherit;
border-block-end-style: inherit;
border-block-end-color: var(--border-color);
border-block-end-width: var(--border-width);
padding: calc(var(--spacing) / 2) var(--spacing);
}
@@ -50,9 +61,9 @@
.footer {
display: block;
border-top: inherit;
border-end-start-radius: var(--inner-border-radius);
border-end-end-radius: var(--inner-border-radius);
border-block-start-style: inherit;
border-block-start-color: var(--border-color);
border-block-start-width: var(--border-width);
padding: var(--spacing);
}

View File

@@ -1,5 +1,6 @@
import { html } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { HasSlotController } from '../../internal/slot.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
import sizeStyles from '../../styles/utilities/size.css';
import styles from './card.css';
@@ -20,25 +21,35 @@ import styles from './card.css';
* @csspart body - The container that wraps the card's main content.
* @csspart footer - The container that wraps the card's footer.
*
* @cssproperty --border-radius - The radius for the card's corners. Expects a single value. Defaults to `var(--wa-panel-border-radius)`.
* @cssproperty --border-width - The width of the card's borders. Expects a single value. Defaults to `var(--wa-panel-border-width)`.
* @cssproperty --spacing - The amount of space around and between sections of the card. Expects a single value. Defaults to `var(--wa-space)`.
* @cssproperty [--border-radius=var(--wa-panel-border-radius)] - The radius for the card's corners. Expects a single value.
* @cssproperty [--border-color=var(--wa-color-surface-border)] - The color of the card's borders, including inner borders. Expects a single value.
* @cssproperty [--border-width=var(--wa-panel-border-width)] - The width of the card's borders. Expects a single value.
* @cssproperty [--spacing=var(--wa-space)] - The amount of space around and between sections of the card. Expects a single value.
*/
@customElement('wa-card')
export default class WaCard extends WebAwesomeElement {
static shadowStyle = [sizeStyles, styles];
private readonly hasSlotController = new HasSlotController(this, 'footer', 'header', 'image');
/** The component's size. Will be inherited by any descendants with a `size` attribute. */
@property({ reflect: true, initial: 'medium' }) size: 'small' | 'medium' | 'large' | 'inherit' = 'inherit';
/** Renders the card with a header. Only needed for SSR, otherwise is automatically added. */
@property({ attribute: 'with-header', type: Boolean }) withHeader = false;
@property({ attribute: 'with-header', type: Boolean, reflect: true }) withHeader = false;
/** Renders the card with an image. Only needed for SSR, otherwise is automatically added. */
@property({ attribute: 'with-image', type: Boolean }) withImage = false;
@property({ attribute: 'with-image', type: Boolean, reflect: true }) withImage = false;
/** Renders the card with a footer. Only needed for SSR, otherwise is automatically added. */
@property({ attribute: 'with-footer', type: Boolean }) withFooter = false;
@property({ attribute: 'with-footer', type: Boolean, reflect: true }) withFooter = false;
updated() {
// Enable the respective slots when detected
if (!this.withHeader && this.hasSlotController.test('header')) this.withHeader = true;
if (!this.withImage && this.hasSlotController.test('image')) this.withImage = true;
if (!this.withFooter && this.hasSlotController.test('footer')) this.withFooter = true;
}
render() {
return html`

View File

@@ -127,7 +127,7 @@ export default class WaCheckbox extends WebAwesomeFormAssociatedElement {
@property({ type: Boolean, reflect: true }) required = false;
/** The checkbox's hint. If you need to display HTML, use the `hint` slot instead. */
@property({ attribute: 'hint' }) hint = '';
@property() hint = '';
private handleClick() {
this.hasInteracted = true;

View File

@@ -55,12 +55,6 @@ details {
padding-block-start: var(--spacing);
}
.show {
}
.hide {
}
@keyframes show {
from {
}

View File

@@ -60,9 +60,16 @@ describe('<wa-menu-item>', () => {
});
});
it('should return a text label when calling getTextLabel()', async () => {
it('defaultLabel should return a text label', async () => {
const el = await fixture<WaMenuItem>(html` <wa-menu-item>Test</wa-menu-item> `);
expect(el.getTextLabel()).to.equal('Test');
expect(el.defaultLabel).to.equal('Test');
expect(el.label).to.equal('Test');
});
it('label attribute should override default label', async () => {
const el = await fixture<WaMenuItem>(html` <wa-menu-item label="Manual label">Text content</wa-menu-item> `);
expect(el.defaultLabel).to.equal('Text content');
expect(el.label).to.equal('Manual label');
});
it('should emit the slotchange event when the label changes', async () => {
@@ -107,7 +114,7 @@ describe('<wa-menu-item>', () => {
expect(submenuSlot.hidden).to.be.true;
});
it('should render an wa-popup if the slot="submenu" attribute is present', async () => {
it('should render a wa-popup if the slot="submenu" attribute is present', async () => {
const menu = await fixture<WaMenuItem>(html`
<wa-menu>
<wa-menu-item id="test">

View File

@@ -1,9 +1,8 @@
import type { PropertyValues } from 'lit';
import { html } from 'lit';
import { customElement, property, query } from 'lit/decorators.js';
import { customElement, property, query, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { getTextContent } from '../../internal/slot.js';
import { watch } from '../../internal/watch.js';
import getText from '../../internal/get-text.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
import { LocalizeController } from '../../utilities/localize.js';
import '../icon/icon.js';
@@ -43,7 +42,6 @@ import { SubmenuController } from './submenu-controller.js';
export default class WaMenuItem extends WebAwesomeElement {
static shadowStyle = styles;
private cachedTextLabel: string;
private readonly localize = new LocalizeController(this);
@query('slot:not([name])') defaultSlot: HTMLSlotElement;
@@ -64,6 +62,36 @@ export default class WaMenuItem extends WebAwesomeElement {
/** Draws the menu item in a disabled state, preventing selection. */
@property({ type: Boolean, reflect: true }) disabled = false;
_label: string = '';
/**
* The options plain text label.
* Usually automatically generated, but can be useful to provide manually for cases involving complex content.
*/
@property()
set label(value) {
const oldValue = this._label;
this._label = value || '';
if (this._label !== oldValue) {
this.requestUpdate('label', oldValue);
}
}
get label(): string {
if (this._label) {
return this._label;
}
if (!this.defaultLabel) {
this.updateDefaultLabel();
}
return this.defaultLabel;
}
/** The default label, generated from the element contents. Will be equal to `label` in most cases. */
@state() defaultLabel = '';
/**
* Used for SSR purposes. If true, will render a ">" caret icon for showing that it has a submenu, but will be non-interactive.
*/
@@ -75,6 +103,7 @@ export default class WaMenuItem extends WebAwesomeElement {
super.connectedCallback();
this.addEventListener('click', this.handleHostClick);
this.addEventListener('mouseover', this.handleMouseOver);
this.updateDefaultLabel();
}
disconnectedCallback() {
@@ -93,17 +122,10 @@ export default class WaMenuItem extends WebAwesomeElement {
}
private handleDefaultSlotChange() {
const textLabel = this.getTextLabel();
// Ignore the first time the label is set
if (typeof this.cachedTextLabel === 'undefined') {
this.cachedTextLabel = textLabel;
return;
}
let labelChanged = this.updateDefaultLabel();
// When the label changes, emit a slotchange event so parent controls see it
if (textLabel !== this.cachedTextLabel) {
this.cachedTextLabel = textLabel;
if (labelChanged) {
/** @internal - prevent the CEM from recording this event */
this.dispatchEvent(new Event('slotchange', { bubbles: true, composed: false, cancelable: false }));
}
@@ -122,41 +144,48 @@ export default class WaMenuItem extends WebAwesomeElement {
event.stopPropagation();
};
@watch('checked')
handleCheckedChange() {
// For proper accessibility, users have to use type="checkbox" to use the checked attribute
if (this.checked && this.type !== 'checkbox') {
this.checked = false;
return;
updated(changedProperties: PropertyValues<this>) {
if (changedProperties.has('checked')) {
// For proper accessibility, users have to use type="checkbox" to use the checked attribute
if (this.checked && this.type !== 'checkbox') {
this.checked = false;
return;
}
// Only checkbox types can receive the aria-checked attribute
if (this.type === 'checkbox') {
this.setAttribute('aria-checked', this.checked ? 'true' : 'false');
} else {
this.removeAttribute('aria-checked');
}
}
// Only checkbox types can receive the aria-checked attribute
if (this.type === 'checkbox') {
this.setAttribute('aria-checked', this.checked ? 'true' : 'false');
} else {
this.removeAttribute('aria-checked');
if (changedProperties.has('disabled')) {
this.setAttribute('aria-disabled', this.disabled ? 'true' : 'false');
}
if (changedProperties.has('type')) {
if (this.type === 'checkbox') {
this.setAttribute('role', 'menuitemcheckbox');
this.setAttribute('aria-checked', this.checked ? 'true' : 'false');
} else {
this.setAttribute('role', 'menuitem');
this.removeAttribute('aria-checked');
}
}
}
@watch('disabled')
handleDisabledChange() {
this.setAttribute('aria-disabled', this.disabled ? 'true' : 'false');
}
private updateDefaultLabel() {
let oldValue = this.defaultLabel;
this.defaultLabel = getText(this).trim();
let changed = this.defaultLabel !== oldValue;
@watch('type')
handleTypeChange() {
if (this.type === 'checkbox') {
this.setAttribute('role', 'menuitemcheckbox');
this.setAttribute('aria-checked', this.checked ? 'true' : 'false');
} else {
this.setAttribute('role', 'menuitem');
this.removeAttribute('aria-checked');
if (!this._label && changed) {
// Uses default label, and it has changed
this.requestUpdate('label', oldValue);
}
}
/** Returns a text label based on the contents of the menu item's default slot. */
getTextLabel() {
return getTextContent(this.defaultSlot);
return changed;
}
isSubmenu() {

View File

@@ -8,13 +8,7 @@
color: var(--wa-color-text-normal);
-webkit-user-select: none;
user-select: none;
}
:host(:focus) {
outline: none;
}
.option {
position: relative;
display: flex;
align-items: center;
@@ -25,19 +19,23 @@
cursor: pointer;
}
:host(:not([disabled])) .option--hover:not(.option--current) {
:host(:focus) {
outline: none;
}
:host(:not([disabled], :state(current)):is(:state(hover), :hover)) {
background-color: var(--background-color-hover);
color: var(--text-color-hover);
}
.option--current,
:host([disabled]) .option--current {
:host(:state(current)),
:host([disabled]:state(current)) {
background-color: var(--background-color-current);
color: var(--text-color-current);
opacity: 1;
}
:host([disabled]) .option {
:host([disabled]) {
outline: none;
opacity: 0.5;
cursor: not-allowed;
@@ -48,7 +46,7 @@
display: inline-block;
}
.option .check {
.check {
flex: 0 0 auto;
display: flex;
align-items: center;
@@ -58,7 +56,7 @@
width: var(--wa-space-xl);
}
.option--selected .check {
:host(:state(selected)) .check {
visibility: visible;
}
@@ -78,7 +76,7 @@
}
@media (forced-colors: active) {
:host(:hover:not([aria-disabled='true'])) .option {
:host(:hover:not([aria-disabled='true'])) {
outline: dashed 1px SelectedItem;
outline-offset: -1px;
}

View File

@@ -23,6 +23,7 @@ describe('<wa-option>', () => {
expect(el.value).to.equal('');
expect(el.disabled).to.be.false;
expect(el.label).to.equal('Test');
expect(el.getAttribute('aria-disabled')).to.equal('false');
});
@@ -44,9 +45,16 @@ describe('<wa-option>', () => {
expect(el.value).to.equal('10');
});
it('should escape HTML when calling getTextLabel()', async () => {
it('defaultLabel should escape HTML', async () => {
const el = await fixture<WaOption>(html` <wa-option><strong>Option</strong></wa-option> `);
expect(el.getTextLabel()).to.equal('Option');
expect(el.defaultLabel).to.equal('Option');
expect(el.label).to.equal('Option');
});
it('label attribute should override default label', async () => {
const el = await fixture<WaOption>(html` <wa-option label="Manual label">Text content</wa-option> `);
expect(el.defaultLabel).to.equal('Text content');
expect(el.label).to.equal('Manual label');
});
});
}

View File

@@ -1,7 +1,7 @@
import type { PropertyValues } from 'lit';
import { html } from 'lit';
import { customElement, property, query, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { watch } from '../../internal/watch.js';
import getText from '../../internal/get-text.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
import { LocalizeController } from '../../utilities/localize.js';
import '../icon/icon.js';
@@ -25,10 +25,13 @@ import styles from './option.css';
* @cssproperty --text-color-hover - The label color on hover.
*
* @csspart checked-icon - The checked icon, a `<wa-icon>` element.
* @csspart base - The component's base wrapper.
* @csspart label - The option's label.
* @csspart prefix - The container that wraps the prefix.
* @csspart suffix - The container that wraps the suffix.
*
* @cssstate current - The user has keyed into the option, but hasn't selected it yet (shows a highlight)
* @cssstate selected - The option is selected and has aria-selected="true"
* @cssstate hover - Like `:hover` but works while dragging in Safari
*/
@customElement('wa-option')
export default class WaOption extends WebAwesomeElement {
@@ -40,9 +43,10 @@ export default class WaOption extends WebAwesomeElement {
@query('.label') defaultSlot: HTMLSlotElement;
@state() current = false; // the user has keyed into the option, but hasn't selected it yet (shows a highlight)
@state() selected = false; // the option is selected and has aria-selected="true"
@state() hasHover = false; // we need this because Safari doesn't honor :hover styles while dragging
// Set via the parent select
@state() current = false;
@state() selected = false;
/**
* The option's value. When selected, the containing form control will receive this value. The value must be unique
@@ -54,13 +58,56 @@ export default class WaOption extends WebAwesomeElement {
/** Draws the option in a disabled state, preventing selection. */
@property({ type: Boolean, reflect: true }) disabled = false;
_label: string = '';
/**
* The options plain text label.
* Usually automatically generated, but can be useful to provide manually for cases involving complex content.
*/
@property()
set label(value) {
const oldValue = this._label;
this._label = value || '';
if (this._label !== oldValue) {
this.requestUpdate('label', oldValue);
}
}
get label(): string {
if (this._label) {
return this._label;
}
if (!this.defaultLabel) {
this.updateDefaultLabel();
}
return this.defaultLabel;
}
/** The default label, generated from the element contents. Will be equal to `label` in most cases. */
@state() defaultLabel = '';
connectedCallback() {
super.connectedCallback();
this.setAttribute('role', 'option');
this.setAttribute('aria-selected', 'false');
this.addEventListener('mouseenter', this.handleHover);
this.addEventListener('mouseleave', this.handleHover);
this.updateDefaultLabel();
}
disconnectedCallback(): void {
super.disconnectedCallback();
this.removeEventListener('mouseenter', this.handleHover);
this.removeEventListener('mouseleave', this.handleHover);
}
private handleDefaultSlotChange() {
this.updateDefaultLabel();
if (this.isInitialized) {
// When the label changes, tell the controller to update
customElements.whenDefined('wa-select').then(() => {
@@ -74,84 +121,75 @@ export default class WaOption extends WebAwesomeElement {
}
}
private handleMouseEnter() {
this.hasHover = true;
}
private handleHover = (event: Event) => {
// We need this because Safari doesn't honor :hover styles while dragging
// Testcase: https://codepen.io/leaverou/pen/VYZOOjy
if (event.type === 'mouseenter') {
this.toggleCustomState('hover', true);
} else if (event.type === 'mouseleave') {
this.toggleCustomState('hover', false);
}
};
private handleMouseLeave() {
this.hasHover = false;
}
updated(changedProperties: PropertyValues<this>) {
super.updated(changedProperties);
@watch('disabled')
handleDisabledChange() {
this.setAttribute('aria-disabled', this.disabled ? 'true' : 'false');
}
@watch('selected')
handleSelectedChange() {
this.setAttribute('aria-selected', this.selected ? 'true' : 'false');
}
@watch('value')
handleValueChange() {
// Ensure the value is a string. This ensures the next line doesn't error and allows framework users to pass numbers
// instead of requiring them to cast the value to a string.
if (typeof this.value !== 'string') {
this.value = String(this.value);
if (changedProperties.has('disabled')) {
this.setAttribute('aria-disabled', this.disabled ? 'true' : 'false');
}
if (this.value.includes(' ')) {
// eslint-disable-next-line no-console
console.error(`Option values cannot include a space. All spaces have been replaced with underscores.`, this);
this.value = this.value.replace(/ /g, '_');
if (changedProperties.has('selected')) {
this.setAttribute('aria-selected', this.selected ? 'true' : 'false');
this.toggleCustomState('selected', this.selected);
}
if (changedProperties.has('value')) {
// Ensure the value is a string. This ensures the next line doesn't error and allows framework users to pass numbers
// instead of requiring them to cast the value to a string.
if (typeof this.value !== 'string') {
this.value = String(this.value);
}
if (this.value.includes(' ')) {
// eslint-disable-next-line no-console
console.error(`Option values cannot include a space. All spaces have been replaced with underscores.`, this);
this.value = this.value.replace(/ /g, '_');
}
this.handleDefaultSlotChange();
}
if (changedProperties.has('current')) {
this.toggleCustomState('current', this.current);
}
}
/** Returns a plain text label based on the option's content. */
getTextLabel() {
const nodes = this.childNodes;
let label = '';
private updateDefaultLabel() {
let oldValue = this.defaultLabel;
this.defaultLabel = getText(this).trim();
let changed = this.defaultLabel !== oldValue;
[...nodes].forEach(node => {
if (node.nodeType === Node.ELEMENT_NODE) {
if (!(node as HTMLElement).hasAttribute('slot')) {
label += (node as HTMLElement).textContent;
}
}
if (!this._label && changed) {
// Uses default label, and it has changed
this.requestUpdate('label', oldValue);
}
if (node.nodeType === Node.TEXT_NODE) {
label += node.textContent;
}
});
return label.trim();
return changed;
}
render() {
return html`
<div
part="base"
class=${classMap({
option: true,
'option--current': this.current,
'option--selected': this.selected,
'option--hover': this.hasHover,
})}
@mouseenter=${this.handleMouseEnter}
@mouseleave=${this.handleMouseLeave}
>
<wa-icon
part="checked-icon"
class="check"
name="check"
library="system"
variant="solid"
aria-hidden="true"
></wa-icon>
<slot part="prefix" name="prefix" class="prefix"></slot>
<slot part="label" class="label" @slotchange=${this.handleDefaultSlotChange}></slot>
<slot part="suffix" name="suffix" class="suffix"></slot>
</div>
<wa-icon
part="checked-icon"
class="check"
name="check"
library="system"
variant="solid"
aria-hidden="true"
></wa-icon>
<slot part="prefix" name="prefix" class="prefix"></slot>
<slot part="label" class="label" @slotchange=${this.handleDefaultSlotChange}></slot>
<slot part="suffix" name="suffix" class="suffix"></slot>
`;
}
}

View File

@@ -9,7 +9,7 @@
--banner-height: 0px;
--header-height: 0px;
--subheader-height: 0px;
--scroll-margin-top: calc(var(--header-height, 0px) + var(--subheader-height, 0px));
--scroll-margin-top: calc(var(--header-height, 0px) + var(--subheader-height, 0px) + 0.5em);
}
slot[name]:not([name='skip-to-content'], [name='navigation-toggle'])::slotted(*) {

View File

@@ -50,6 +50,7 @@ import styles from './radio-button.css';
@customElement('wa-radio-button')
export default class WaRadioButton extends WebAwesomeFormAssociatedElement {
static shadowStyle = [variantStyles, appearanceStyles, sizeStyles, nativeStyles, buttonStyles, styles];
static rectProxy = 'input';
private readonly hasSlotController = new HasSlotController(this, '[default]', 'prefix', 'suffix');

View File

@@ -1,15 +1,15 @@
:host(:focus-visible) {
outline: none;
}
.radio {
display: flex;
:host {
display: grid;
grid-template-columns: auto 1fr;
align-items: top;
font: inherit;
vertical-align: middle;
cursor: pointer;
}
:host(:focus-visible) {
outline: none;
}
.checked-icon {
display: flex;
fill: currentColor;
@@ -19,6 +19,11 @@
}
/* When the control isn't checked, hide the circle for Windows High Contrast mode a11y */
.radio:not(.radio--checked) svg circle {
:host(:not(:state(checked))) svg circle {
opacity: 0;
}
[part~='hint'] {
grid-column: 2;
margin-block-start: var(--wa-space-3xs);
}

View File

@@ -1,9 +1,11 @@
import type { PropertyValues } from 'lit';
import { html, isServer } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { watch } from '../../internal/watch.js';
import { HasSlotController } from '../../internal/slot.js';
import { WebAwesomeFormAssociatedElement } from '../../internal/webawesome-form-associated-element.js';
import nativeStyles from '../../styles/native/radio.css';
import formControlStyles from '../../styles/shadow/form-control.css';
import sizeStyles from '../../styles/utilities/size.css';
import '../icon/icon.js';
import styles from './radio.css';
@@ -17,14 +19,15 @@ import styles from './radio.css';
* @dependency wa-icon
*
* @slot - The radio's label.
* @slot hint - Text that describes how to use the checkbox. Alternatively, you can use the `hint` attribute.
*
* @event blur - Emitted when the control loses focus.
* @event focus - Emitted when the control gains focus.
*
* @csspart base - The component's base wrapper.
* @csspart control - The circular container that wraps the radio's checked state.
* @csspart checked-icon - The checked icon.
* @csspart label - The container that wraps the radio's label.
* @csspart hint - The hint's wrapper.
*
* @cssproperty --background-color - The radio's background color.
* @cssproperty --background-color-checked - The radio's background color when checked.
@@ -42,7 +45,7 @@ import styles from './radio.css';
*/
@customElement('wa-radio')
export default class WaRadio extends WebAwesomeFormAssociatedElement {
static shadowStyle = [sizeStyles, nativeStyles, styles];
static shadowStyle = [formControlStyles, sizeStyles, nativeStyles, styles];
@state() checked = false;
@@ -63,6 +66,11 @@ export default class WaRadio extends WebAwesomeFormAssociatedElement {
/** Disables the radio. */
@property({ type: Boolean }) disabled = false;
/** The radio's hint. If you need to display HTML, use the `hint` slot instead. */
@property() hint = '';
private readonly hasSlotController = new HasSlotController(this, 'hint');
constructor() {
super();
if (!isServer) {
@@ -81,11 +89,19 @@ export default class WaRadio extends WebAwesomeFormAssociatedElement {
this.setAttribute('aria-disabled', this.disabled ? 'true' : 'false');
}
@watch('checked')
handleCheckedChange() {
this.toggleCustomState('checked', this.checked);
this.setAttribute('aria-checked', this.checked ? 'true' : 'false');
this.tabIndex = this.checked ? 0 : -1;
updated(changedProperties: PropertyValues<this>) {
super.updated(changedProperties);
if (changedProperties.has('checked')) {
this.toggleCustomState('checked', this.checked);
this.setAttribute('aria-checked', this.checked ? 'true' : 'false');
this.tabIndex = this.checked ? 0 : -1;
}
if (changedProperties.has('disabled')) {
this.toggleCustomState('disabled', this.disabled);
this.setAttribute('aria-disabled', this.disabled ? 'true' : 'false');
}
}
/**
@@ -95,12 +111,6 @@ export default class WaRadio extends WebAwesomeFormAssociatedElement {
// We override `setValue` because we don't want to set form values from here. We want to do that in "RadioGroup" itself.
}
@watch('disabled', { waitUntilFirstUpdate: true })
handleDisabledChange() {
this.toggleCustomState('disabled', this.disabled);
this.setAttribute('aria-disabled', this.disabled ? 'true' : 'false');
}
private handleClick = () => {
if (!this.disabled) {
this.checked = true;
@@ -108,26 +118,30 @@ export default class WaRadio extends WebAwesomeFormAssociatedElement {
};
render() {
return html`
<span
part="base"
class=${classMap({
radio: true,
'radio--checked': this.checked,
})}
>
<span part="control" class="control">
${this.checked
? html`
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" part="checked-icon" class="checked-icon">
<circle cx="8" cy="8" r="8" />
</svg>
`
: ''}
</span>
const hasHintSlot = isServer ? true : this.hasSlotController.test('hint');
const hasHint = this.hint ? true : !!hasHintSlot;
<slot part="label" class="label"></slot>
return html`
<span part="control" class="control">
${this.checked
? html`
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" part="checked-icon" class="checked-icon">
<circle cx="8" cy="8" r="8" />
</svg>
`
: ''}
</span>
<slot part="label" class="label"></slot>
<slot
name="hint"
aria-hidden=${hasHint ? 'false' : 'true'}
class="${classMap({ 'has-slotted': hasHint })}"
id="hint"
part="hint"
>${this.hint}</slot
>
`;
}
}

View File

@@ -72,7 +72,7 @@
}
&::placeholder {
color: var(--wa-form-controls-placeholder-color);
color: var(--wa-form-control-placeholder-color);
}
}
@@ -177,7 +177,7 @@
align-items: center;
color: var(--wa-color-neutral-on-quiet);
transition: rotate var(--wa-transition-slow) ease;
rotate: 0;
rotate: 0deg;
margin-inline-start: var(--wa-space-s);
.open & {
@@ -194,7 +194,7 @@
background: var(--wa-color-surface-raised);
border-color: var(--wa-color-surface-border);
border-radius: var(--wa-border-radius-m);
border-style: var(--border-style);
border-style: var(--wa-border-style);
border-width: var(--border-width);
padding-block: var(--wa-space-xs);
padding-inline: 0;
@@ -208,13 +208,13 @@
&::slotted(wa-divider) {
--spacing: var(--wa-space-xs);
}
&::slotted(small) {
display: block;
font-size: var(--wa-font-size-s);
font-weight: var(--wa-font-weight-semibold);
color: var(--wa-color-text-quiet);
padding-block: var(--wa-space-xs);
padding-inline: var(--wa-space-xl);
}
}
slot:not([name])::slotted(small) {
display: block;
font-size: var(--wa-font-size-s);
font-weight: var(--wa-font-weight-semibold);
color: var(--wa-color-text-quiet);
padding-block: var(--wa-space-xs);
padding-inline: var(--wa-space-xl);
}

View File

@@ -68,7 +68,6 @@ import styles from './select.css';
* @csspart listbox - The listbox container where options are slotted.
* @csspart tags - The container that houses option tags when `multiselect` is used.
* @csspart tag - The individual tags that represent each multiselect option.
* @csspart tag__base - The tag's base part.
* @csspart tag__content - The tag's content part.
* @csspart tag__remove-button - The tag's remove button.
* @csspart tag__remove-button__base - The tag's remove button base part.
@@ -253,7 +252,7 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
removable
@wa-remove=${(event: WaRemoveEvent) => this.handleTagRemove(event, option)}
>
${option.getTextLabel()}
${option.label}
</wa-tag>
`;
};
@@ -438,7 +437,7 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
}
for (const option of allOptions) {
const label = option.getTextLabel().toLowerCase();
const label = option.label.toLowerCase();
if (label.startsWith(this.typeToSelectString)) {
this.setCurrentOption(option);
@@ -643,7 +642,7 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
} else {
const selectedOption = this.selectedOptions[0];
this.value = selectedOption?.value ?? '';
this.displayLabel = selectedOption?.getTextLabel?.() ?? '';
this.displayLabel = selectedOption?.label ?? '';
}
// Update validity
@@ -661,7 +660,18 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
</div>`;
} else if (index === this.maxOptionsVisible) {
// Hit tag limit
return html`<wa-tag>+${this.selectedOptions.length - index}</wa-tag>`;
return html`
<wa-tag
part="tag"
exportparts="
base:tag__base,
content:tag__content,
remove-button:tag__remove-button,
remove-button__base:tag__remove-button__base
"
>+${this.selectedOptions.length - index}</wa-tag
>
`;
}
return html``;
});
@@ -917,6 +927,7 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
</div>
<slot
id="hint"
name="hint"
part="hint"
class=${classMap({

View File

@@ -24,7 +24,6 @@ let id = 0;
@customElement('wa-tab')
export default class WaTab extends WebAwesomeElement {
static shadowStyle = styles;
public slot = 'nav'; // Auto-slot into nav slot
private readonly attrId = ++id;
private readonly componentId = `wa-tab-${this.attrId}`;
@@ -47,6 +46,9 @@ export default class WaTab extends WebAwesomeElement {
@property({ type: Number, reflect: true }) tabIndex = 0;
connectedCallback() {
// Auto-slot into nav slot
this.slot ||= 'nav';
super.connectedCallback();
this.setAttribute('role', 'tab');
}

View File

@@ -4,10 +4,16 @@
--max-width: 30ch;
--padding: var(--wa-space-2xs) var(--wa-space-xs);
/** These styles are added so we don't interfere in the DOM. */
display: inline-block;
position: absolute;
/** These styles are added so we dont interfere in the DOM. */
/** Defaults for inherited CSS properties */
color: var(--wa-tooltip-content-color);
font-size: var(--wa-tooltip-font-size);
line-height: var(--wa-tooltip-line-height);
text-align: start;
white-space: normal;
}
.tooltip {
@@ -41,12 +47,6 @@
max-width: var(--max-width);
border-radius: var(--border-radius);
background-color: var(--background-color);
font: inherit;
color: var(--wa-tooltip-content-color);
font-size: var(--wa-tooltip-font-size);
line-height: var(--wa-tooltip-line-height);
text-align: start;
white-space: normal;
padding: var(--padding);
user-select: none;
-webkit-user-select: none;

View File

@@ -35,11 +35,8 @@ import styles from './tooltip.css';
*
* @cssproperty --background-color - The tooltip's background color.
* @cssproperty --border-radius - The radius of the tooltip's corners.
* @cssproperty --text-color - The color of the tooltip's content.
* @cssproperty --max-width - The maximum width of the tooltip before its content will wrap.
* @cssproperty --padding - The padding within the tooltip.
* @cssproperty --hide-delay - The amount of time to wait before hiding the tooltip when hovering.
* @cssproperty --show-delay - The amount of time to wait before showing the tooltip when hovering.
*/
@customElement('wa-tooltip')
export default class WaTooltip extends WebAwesomeElement {

46
src/internal/get-text.ts Normal file
View File

@@ -0,0 +1,46 @@
/**
* Like textContent, but better:
* - Uses assignedNodes to get text content from slots (and falls back to content if nothing is slotted)
* - Ignores script and style elements
* @param root - One or more nodes to get text content from.
* @param depth - By default, will just return element.textContent for any child elements instead of calling the function recursively.
* Set to a positive integer to recurse that many levels. Generally a tradeoff between performance and accuracy.
* @returns
*/
export default function getText(root: Node | Iterable<Node>, depth = 0): string {
if (!root || !globalThis.Node) {
return '';
}
if (typeof (root as any)[Symbol.iterator] === 'function') {
let nodes = Array.isArray(root) ? root : [...(root as Iterable<Node>)];
return nodes.map(node => getText(node, --depth)).join('');
}
let node = root as Node;
if (node.nodeType === Node.TEXT_NODE) {
return node.textContent ?? '';
}
if (node.nodeType === Node.ELEMENT_NODE) {
let element = node as HTMLElement;
if (element.hasAttribute('slot') || element.matches('style, script')) {
return '';
}
if (element instanceof HTMLSlotElement) {
let assignedNodes = element.assignedNodes({ flatten: true });
if (assignedNodes.length > 0) {
// If no assigned nodes, we still want the slot contents
return getText(assignedNodes, --depth);
}
}
return depth > -1 ? getText(element, --depth) : (element.textContent ?? '');
}
return node.hasChildNodes() ? getText(node.childNodes, --depth) : '';
}

View File

@@ -12,11 +12,11 @@ export class HasSlotController implements ReactiveController {
private hasDefaultSlot() {
return [...this.host.childNodes].some(node => {
if (node.nodeType === node.TEXT_NODE && node.textContent!.trim() !== '') {
if (node.nodeType === Node.TEXT_NODE && node.textContent!.trim() !== '') {
return true;
}
if (node.nodeType === node.ELEMENT_NODE) {
if (node.nodeType === Node.ELEMENT_NODE) {
const el = node as HTMLElement;
const tagName = el.tagName.toLowerCase();
@@ -90,23 +90,3 @@ export function getInnerHTML(nodes: Iterable<Node>, callback?: (node: Node) => s
return html;
}
/**
* Given a slot, this function iterates over all of its assigned text nodes and returns the concatenated text as a
* string. This is useful because we can't use slot.textContent as an alternative.
*/
export function getTextContent(slot: HTMLSlotElement | undefined | null): string {
if (!slot) {
return '';
}
const nodes = slot.assignedNodes({ flatten: true });
let text = '';
[...nodes].forEach(node => {
if (node.nodeType === Node.TEXT_NODE) {
text += node.textContent;
}
});
return text;
}

View File

@@ -204,6 +204,32 @@ export default class WebAwesomeElement extends LitElement {
);
}
getBoundingClientRect(): DOMRect {
let rect = super.getBoundingClientRect();
if (rect.width === 0 && rect.height === 0) {
let Self = this.constructor as typeof WebAwesomeElement;
if (Self.rectProxy) {
let element = this[Self.rectProxy as keyof this];
if (element instanceof Element) {
let childRect = element.getBoundingClientRect();
if (childRect.width > 0 || childRect.height > 0) {
return childRect;
}
}
}
}
return rect;
}
/**
* If getBoundingClientRect() returns an empty rect,
* should we check another element?
*/
static rectProxy: undefined | string;
static createProperty(name: PropertyKey, options?: PropertyDeclaration): void {
if (options) {
if (options.initial !== undefined && options.default === undefined) {

74
src/styles/brand/base.css Normal file
View File

@@ -0,0 +1,74 @@
:where(:root),
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']),
:where([class^='wa-palette-'], [class*=' wa-palette-']),
:where([class^='wa-brand-'], [class*=' wa-brand-']) {
/**
* Conditional tokens for use in color-mix()
* --wa-color-brand-if-lt-N ➡️ 100% if key < N, 0% otherwise
* --wa-color-brand-if-gte-N ➡️ 100% if key >= N, 0% otherwise
*/
--wa-color-brand-if-lt-40: calc(clamp(0, 40 - var(--wa-color-brand-key), 1) * 100%);
--wa-color-brand-if-lt-50: calc(clamp(0, 50 - var(--wa-color-brand-key), 1) * 100%);
--wa-color-brand-if-lt-60: calc(clamp(0, 60 - var(--wa-color-brand-key), 1) * 100%);
--wa-color-brand-if-lt-70: calc(clamp(0, 70 - var(--wa-color-brand-key), 1) * 100%);
--wa-color-brand-if-lt-80: calc(clamp(0, 80 - var(--wa-color-brand-key), 1) * 100%);
--wa-color-brand-if-gte-40: calc(100% - var(--wa-color-brand-if-lt-40));
--wa-color-brand-if-gte-50: calc(100% - var(--wa-color-brand-if-lt-50));
--wa-color-brand-if-gte-60: calc(100% - var(--wa-color-brand-if-lt-60));
--wa-color-brand-if-gte-70: calc(100% - var(--wa-color-brand-if-lt-70));
--wa-color-brand-if-gte-80: calc(100% - var(--wa-color-brand-if-lt-80));
/*
* Convenience tokens for common tint cutoffs
* --wa-color-brand-N-max ➡️ var(--color-brand) if key <= N, var(--color-brand-N) otherwise
* --wa-color-brand-N-min ➡️ var(--color-brand) if key >= N, var(--color-brand-N) otherwise
*/
--wa-color-brand-40-max: color-mix(
in oklab,
var(--wa-color-brand) var(--wa-color-brand-if-lt-40),
var(--wa-color-brand-40)
);
--wa-color-brand-40-min: color-mix(
in oklab,
var(--wa-color-brand) var(--wa-color-brand-if-gte-40),
var(--wa-color-brand-40)
);
--wa-color-brand-50-max: color-mix(
in oklab,
var(--wa-color-brand) var(--wa-color-brand-if-lt-50),
var(--wa-color-brand-50)
);
--wa-color-brand-50-min: color-mix(
in oklab,
var(--wa-color-brand) var(--wa-color-brand-if-gte-50),
var(--wa-color-brand-50)
);
--wa-color-brand-60-max: color-mix(
in oklab,
var(--wa-color-brand) var(--wa-color-brand-if-lt-60),
var(--wa-color-brand-60)
);
--wa-color-brand-60-min: color-mix(
in oklab,
var(--wa-color-brand) var(--wa-color-brand-if-gte-60),
var(--wa-color-brand-60)
);
--wa-color-brand-70-max: color-mix(
in oklab,
var(--wa-color-brand) var(--wa-color-brand-if-lt-70),
var(--wa-color-brand-70)
);
--wa-color-brand-70-min: color-mix(
in oklab,
var(--wa-color-brand) var(--wa-color-brand-if-gte-70),
var(--wa-color-brand-70)
);
/* Text color: white if key < 60, brand-10 otherwise */
--wa-color-brand-on: color-mix(in oklab, var(--wa-color-brand-10) var(--wa-color-brand-if-gte-60), white);
}

21
src/styles/brand/blue.css Normal file
View File

@@ -0,0 +1,21 @@
@import url('base.css');
:where(:root),
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']),
:where([class^='wa-palette-'], [class*=' wa-palette-']),
.wa-brand-blue {
--wa-color-brand-95: var(--wa-color-blue-95);
--wa-color-brand-90: var(--wa-color-blue-90);
--wa-color-brand-80: var(--wa-color-blue-80);
--wa-color-brand-70: var(--wa-color-blue-70);
--wa-color-brand-60: var(--wa-color-blue-60);
--wa-color-brand-50: var(--wa-color-blue-50);
--wa-color-brand-40: var(--wa-color-blue-40);
--wa-color-brand-30: var(--wa-color-blue-30);
--wa-color-brand-20: var(--wa-color-blue-20);
--wa-color-brand-10: var(--wa-color-blue-10);
--wa-color-brand-05: var(--wa-color-blue-05);
--wa-color-brand: var(--wa-color-blue);
--wa-color-brand-key: var(--wa-color-blue-key);
}

21
src/styles/brand/cyan.css Normal file
View File

@@ -0,0 +1,21 @@
@import url('base.css');
:where(:root),
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']),
:where([class^='wa-palette-'], [class*=' wa-palette-']),
.wa-brand-cyan {
--wa-color-brand-95: var(--wa-color-cyan-95);
--wa-color-brand-90: var(--wa-color-cyan-90);
--wa-color-brand-80: var(--wa-color-cyan-80);
--wa-color-brand-70: var(--wa-color-cyan-70);
--wa-color-brand-60: var(--wa-color-cyan-60);
--wa-color-brand-50: var(--wa-color-cyan-50);
--wa-color-brand-40: var(--wa-color-cyan-40);
--wa-color-brand-30: var(--wa-color-cyan-30);
--wa-color-brand-20: var(--wa-color-cyan-20);
--wa-color-brand-10: var(--wa-color-cyan-10);
--wa-color-brand-05: var(--wa-color-cyan-05);
--wa-color-brand: var(--wa-color-cyan);
--wa-color-brand-key: var(--wa-color-cyan-key);
}

21
src/styles/brand/gray.css Normal file
View File

@@ -0,0 +1,21 @@
@import url('base.css');
:where(:root),
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']),
:where([class^='wa-palette-'], [class*=' wa-palette-']),
.wa-brand-gray {
--wa-color-brand-95: var(--wa-color-gray-95);
--wa-color-brand-90: var(--wa-color-gray-90);
--wa-color-brand-80: var(--wa-color-gray-80);
--wa-color-brand-70: var(--wa-color-gray-70);
--wa-color-brand-60: var(--wa-color-gray-60);
--wa-color-brand-50: var(--wa-color-gray-50);
--wa-color-brand-40: var(--wa-color-gray-40);
--wa-color-brand-30: var(--wa-color-gray-30);
--wa-color-brand-20: var(--wa-color-gray-20);
--wa-color-brand-10: var(--wa-color-gray-10);
--wa-color-brand-05: var(--wa-color-gray-05);
--wa-color-brand: var(--wa-color-gray);
--wa-color-brand-key: var(--wa-color-gray-key);
}

View File

@@ -0,0 +1,21 @@
@import url('base.css');
:where(:root),
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']),
:where([class^='wa-palette-'], [class*=' wa-palette-']),
.wa-brand-green {
--wa-color-brand-95: var(--wa-color-green-95);
--wa-color-brand-90: var(--wa-color-green-90);
--wa-color-brand-80: var(--wa-color-green-80);
--wa-color-brand-70: var(--wa-color-green-70);
--wa-color-brand-60: var(--wa-color-green-60);
--wa-color-brand-50: var(--wa-color-green-50);
--wa-color-brand-40: var(--wa-color-green-40);
--wa-color-brand-30: var(--wa-color-green-30);
--wa-color-brand-20: var(--wa-color-green-20);
--wa-color-brand-10: var(--wa-color-green-10);
--wa-color-brand-05: var(--wa-color-green-05);
--wa-color-brand: var(--wa-color-green);
--wa-color-brand-key: var(--wa-color-green-key);
}

View File

@@ -0,0 +1,21 @@
@import url('base.css');
:where(:root),
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']),
:where([class^='wa-palette-'], [class*=' wa-palette-']),
.wa-brand-indigo {
--wa-color-brand-95: var(--wa-color-indigo-95);
--wa-color-brand-90: var(--wa-color-indigo-90);
--wa-color-brand-80: var(--wa-color-indigo-80);
--wa-color-brand-70: var(--wa-color-indigo-70);
--wa-color-brand-60: var(--wa-color-indigo-60);
--wa-color-brand-50: var(--wa-color-indigo-50);
--wa-color-brand-40: var(--wa-color-indigo-40);
--wa-color-brand-30: var(--wa-color-indigo-30);
--wa-color-brand-20: var(--wa-color-indigo-20);
--wa-color-brand-10: var(--wa-color-indigo-10);
--wa-color-brand-05: var(--wa-color-indigo-05);
--wa-color-brand: var(--wa-color-indigo);
--wa-color-brand-key: var(--wa-color-indigo-key);
}

View File

@@ -0,0 +1,21 @@
@import url('base.css');
:where(:root),
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']),
:where([class^='wa-palette-'], [class*=' wa-palette-']),
.wa-brand-orange {
--wa-color-brand-95: var(--wa-color-orange-95);
--wa-color-brand-90: var(--wa-color-orange-90);
--wa-color-brand-80: var(--wa-color-orange-80);
--wa-color-brand-70: var(--wa-color-orange-70);
--wa-color-brand-60: var(--wa-color-orange-60);
--wa-color-brand-50: var(--wa-color-orange-50);
--wa-color-brand-40: var(--wa-color-orange-40);
--wa-color-brand-30: var(--wa-color-orange-30);
--wa-color-brand-20: var(--wa-color-orange-20);
--wa-color-brand-10: var(--wa-color-orange-10);
--wa-color-brand-05: var(--wa-color-orange-05);
--wa-color-brand: var(--wa-color-orange);
--wa-color-brand-key: var(--wa-color-orange-key);
}

21
src/styles/brand/pink.css Normal file
View File

@@ -0,0 +1,21 @@
@import url('base.css');
:where(:root),
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']),
:where([class^='wa-palette-'], [class*=' wa-palette-']),
.wa-brand-pink {
--wa-color-brand-95: var(--wa-color-pink-95);
--wa-color-brand-90: var(--wa-color-pink-90);
--wa-color-brand-80: var(--wa-color-pink-80);
--wa-color-brand-70: var(--wa-color-pink-70);
--wa-color-brand-60: var(--wa-color-pink-60);
--wa-color-brand-50: var(--wa-color-pink-50);
--wa-color-brand-40: var(--wa-color-pink-40);
--wa-color-brand-30: var(--wa-color-pink-30);
--wa-color-brand-20: var(--wa-color-pink-20);
--wa-color-brand-10: var(--wa-color-pink-10);
--wa-color-brand-05: var(--wa-color-pink-05);
--wa-color-brand: var(--wa-color-pink);
--wa-color-brand-key: var(--wa-color-pink-key);
}

View File

@@ -0,0 +1,21 @@
@import url('base.css');
:where(:root),
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']),
:where([class^='wa-palette-'], [class*=' wa-palette-']),
.wa-brand-purple {
--wa-color-brand-95: var(--wa-color-purple-95);
--wa-color-brand-90: var(--wa-color-purple-90);
--wa-color-brand-80: var(--wa-color-purple-80);
--wa-color-brand-70: var(--wa-color-purple-70);
--wa-color-brand-60: var(--wa-color-purple-60);
--wa-color-brand-50: var(--wa-color-purple-50);
--wa-color-brand-40: var(--wa-color-purple-40);
--wa-color-brand-30: var(--wa-color-purple-30);
--wa-color-brand-20: var(--wa-color-purple-20);
--wa-color-brand-10: var(--wa-color-purple-10);
--wa-color-brand-05: var(--wa-color-purple-05);
--wa-color-brand: var(--wa-color-purple);
--wa-color-brand-key: var(--wa-color-purple-key);
}

21
src/styles/brand/red.css Normal file
View File

@@ -0,0 +1,21 @@
@import url('base.css');
:where(:root),
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']),
:where([class^='wa-palette-'], [class*=' wa-palette-']),
.wa-brand-red {
--wa-color-brand-95: var(--wa-color-red-95);
--wa-color-brand-90: var(--wa-color-red-90);
--wa-color-brand-80: var(--wa-color-red-80);
--wa-color-brand-70: var(--wa-color-red-70);
--wa-color-brand-60: var(--wa-color-red-60);
--wa-color-brand-50: var(--wa-color-red-50);
--wa-color-brand-40: var(--wa-color-red-40);
--wa-color-brand-30: var(--wa-color-red-30);
--wa-color-brand-20: var(--wa-color-red-20);
--wa-color-brand-10: var(--wa-color-red-10);
--wa-color-brand-05: var(--wa-color-red-05);
--wa-color-brand: var(--wa-color-red);
--wa-color-brand-key: var(--wa-color-red-key);
}

View File

@@ -0,0 +1,21 @@
@import url('base.css');
:where(:root),
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']),
:where([class^='wa-palette-'], [class*=' wa-palette-']),
.wa-brand-yellow {
--wa-color-brand-95: var(--wa-color-yellow-95);
--wa-color-brand-90: var(--wa-color-yellow-90);
--wa-color-brand-80: var(--wa-color-yellow-80);
--wa-color-brand-70: var(--wa-color-yellow-70);
--wa-color-brand-60: var(--wa-color-yellow-60);
--wa-color-brand-50: var(--wa-color-yellow-50);
--wa-color-brand-40: var(--wa-color-yellow-40);
--wa-color-brand-30: var(--wa-color-yellow-30);
--wa-color-brand-20: var(--wa-color-yellow-20);
--wa-color-brand-10: var(--wa-color-yellow-10);
--wa-color-brand-05: var(--wa-color-yellow-05);
--wa-color-brand: var(--wa-color-yellow);
--wa-color-brand-key: var(--wa-color-yellow-key);
}

View File

@@ -2,107 +2,143 @@
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']),
.wa-palette-anodized {
--wa-color-red-95: #fbeeeb /* oklch(95.851% 0.01469 33.071) */;
--wa-color-red-90: #f8e0d9 /* oklch(92.431% 0.02823 36.865) */;
--wa-color-red-80: #efbdb1 /* oklch(83.972% 0.06035 33.801) */;
--wa-color-red-70: #e39f8d /* oklch(76.38% 0.08577 35.424) */;
--wa-color-red-60: #d1806b /* oklch(68.083% 0.10573 35.329) */;
--wa-color-red-50: #b35e49 /* oklch(57.764% 0.11532 34.927) */;
--wa-color-red-40: #8e4330 /* oklch(47.527% 0.10671 35.325) */;
--wa-color-red-30: #733223 /* oklch(40.293% 0.09564 34.326) */;
--wa-color-red-20: #562317 /* oklch(32.748% 0.07905 34.548) */;
--wa-color-red-10: #36130a /* oklch(23.979% 0.05887 35.518) */;
--wa-color-red-05: #240a05 /* oklch(18.708% 0.04625 34.412) */;
--wa-color-red: var(--wa-color-red-50);
--wa-color-red-95: #ffefee /* oklch(96.448% 0.01742 21.806) */;
--wa-color-red-90: #fededd /* oklch(92.674% 0.03556 19.95) */;
--wa-color-red-80: #fabab8 /* oklch(84.695% 0.0745 20.791) */;
--wa-color-red-70: #f19695 /* oklch(76.595% 0.11008 20.609) */;
--wa-color-red-60: #d47c7e /* oklch(68.199% 0.10963 18.885) */;
--wa-color-red-50: #af5e5a /* oklch(57.551% 0.10599 23.859) */;
--wa-color-red-40: #89453f /* oklch(47.393% 0.09388 26.419) */;
--wa-color-red-30: #6d352f /* oklch(40.077% 0.08085 27.557) */;
--wa-color-red-20: #522521 /* oklch(32.622% 0.06818 26.677) */;
--wa-color-red-10: #331512 /* oklch(23.979% 0.04918 27.221) */;
--wa-color-red-05: #220b09 /* oklch(18.625% 0.04007 26.766) */;
--wa-color-red: var(--wa-color-red-70);
--wa-color-red-key: 70;
--wa-color-yellow-95: #fff2b8 /* oklch(95.764% 0.07527 96.955) */;
--wa-color-yellow-90: #ffe578 /* oklch(92.163% 0.13215 96.194) */;
--wa-color-yellow-80: #eac46c /* oklch(83.54% 0.11551 86.482) */;
--wa-color-yellow-70: #d5a766 /* oklch(75.67% 0.0989 74.916) */;
--wa-color-yellow-60: #bd8a5e /* oklch(67.327% 0.08618 61.351) */;
--wa-color-yellow-50: #9d6a4f /* oklch(57.149% 0.0766 48.607) */;
--wa-color-yellow-40: #794f3c /* oklch(46.965% 0.06418 45.076) */;
--wa-color-yellow-30: #603d2f /* oklch(39.723% 0.05466 42.556) */;
--wa-color-yellow-20: #482b21 /* oklch(32.256% 0.04681 39.773) */;
--wa-color-yellow-10: #2c1912 /* oklch(23.678% 0.03339 40.974) */;
--wa-color-yellow-05: #1d0e0a /* oklch(18.423% 0.02718 35.681) */;
--wa-color-yellow: var(--wa-color-yellow-90);
--wa-color-orange-95: oklch(96.462% 0.02077 52.138);
--wa-color-orange-90: oklch(92.556% 0.04363 51.242);
--wa-color-orange-80: #fdbb96 /* oklch(84.396% 0.09052 50.397) */;
--wa-color-orange-70: #eb9c74 /* oklch(76.151% 0.10953 47.299) */;
--wa-color-orange-60: #cf8162 /* oklch(67.86% 0.10698 42.148) */;
--wa-color-orange-50: #aa6248 /* oklch(57.281% 0.1014 40.415) */;
--wa-color-orange-40: #864834 /* oklch(47.233% 0.08958 39.166) */;
--wa-color-orange-30: #6b3727 /* oklch(39.974% 0.07776 38.975) */;
--wa-color-orange-20: #50271a /* oklch(32.519% 0.0649 38.022) */;
--wa-color-orange-10: #32160e /* oklch(23.873% 0.04713 38.423) */;
--wa-color-orange-05: #210c06 /* oklch(18.614% 0.03797 38.589) */;
--wa-color-orange: var(--wa-color-orange-70);
--wa-color-orange-key: 70;
--wa-color-green-95: #dcf8ea /* oklch(95.5% 0.03463 164.16) */;
--wa-color-green-90: #bcf1d8 /* oklch(91.473% 0.06399 164.58) */;
--wa-color-green-80: #6dddad /* oklch(81.852% 0.12484 163.42) */;
--wa-color-green-70: #33c685 /* oklch(73.519% 0.15428 158.98) */;
--wa-color-green-60: #0a6 /* oklch(64.917% 0.15588 156.54) */;
--wa-color-green-50: #008754 /* oklch(54.923% 0.12782 158.29) */;
--wa-color-green-40: #006646 /* oklch(45.146% 0.09722 163.25) */;
--wa-color-green-30: #00503b /* oklch(38.299% 0.07764 167.91) */;
--wa-color-green-20: #003a2d /* oklch(31.045% 0.06006 172.22) */;
--wa-color-green-10: #00231b /* oklch(22.853% 0.04344 174.1) */;
--wa-color-green-05: #001610 /* oklch(17.856% 0.03405 173.73) */;
--wa-color-green: var(--wa-color-green-60);
--wa-color-yellow-95: #faf3e1 /* oklch(96.479% 0.02487 89.211) */;
--wa-color-yellow-90: #f4e5be /* oklch(92.412% 0.0535 89.488) */;
--wa-color-yellow-80: #eac673 /* oklch(84.03% 0.1101 86.581) */;
--wa-color-yellow-70: #d4a85b /* oklch(75.609% 0.10842 79.92) */;
--wa-color-yellow-60: #bf8b4a /* oklch(67.445% 0.10375 70.58) */;
--wa-color-yellow-50: #a06938 /* oklch(56.952% 0.09579 60.65) */;
--wa-color-yellow-40: #7f4d29 /* oklch(47.038% 0.08433 54.746) */;
--wa-color-yellow-30: #663b1e /* oklch(39.848% 0.07399 52.93) */;
--wa-color-yellow-20: #4c2a14 /* oklch(32.393% 0.0609 51.89) */;
--wa-color-yellow-10: #2f1809 /* oklch(23.743% 0.04463 52.113) */;
--wa-color-yellow-05: #1f0e04 /* oklch(18.6% 0.03542 53.04) */;
--wa-color-yellow: var(--wa-color-yellow-80);
--wa-color-yellow-key: 80;
--wa-color-teal-95: #cbfaf9 /* oklch(95.226% 0.04791 194.77) */;
--wa-color-teal-90: #9ff4f3 /* oklch(91.294% 0.08228 194.86) */;
--wa-color-teal-80: #1adfdb /* oklch(81.816% 0.13749 192.46) */;
--wa-color-teal-70: #00c3be /* oklch(73.815% 0.1269 191.5) */;
--wa-color-teal-60: #1aa6a0 /* oklch(65.653% 0.10892 190.15) */;
--wa-color-teal-50: #15837e /* oklch(55.258% 0.09092 189.97) */;
--wa-color-teal-40: #10635e /* oklch(45.271% 0.07391 188.55) */;
--wa-color-teal-30: #0c4e4a /* oklch(38.439% 0.06211 188.63) */;
--wa-color-teal-20: #083936 /* oklch(31.289% 0.04971 188.74) */;
--wa-color-teal-10: #04221f /* oklch(22.881% 0.03588 185.7) */;
--wa-color-teal-05: #021513 /* oklch(17.794% 0.0276 186.43) */;
--wa-color-teal: var(--wa-color-teal-80);
--wa-color-green-95: #ebf6e0 /* oklch(95.901% 0.03126 128.83) */;
--wa-color-green-90: #d3efbe /* oklch(91.922% 0.0713 131.97) */;
--wa-color-green-80: #96db86 /* oklch(82.501% 0.13433 139.79) */;
--wa-color-green-70: #68c17c /* oklch(73.758% 0.13164 149.51) */;
--wa-color-green-60: #4aa672 /* oklch(65.518% 0.11814 156.17) */;
--wa-color-green-50: #2e845d /* oklch(55.093% 0.10286 160.26) */;
--wa-color-green-40: #1b6548 /* oklch(45.377% 0.0863 162.75) */;
--wa-color-green-30: #104f38 /* oklch(38.219% 0.07374 163.76) */;
--wa-color-green-20: #083a29 /* oklch(31.135% 0.05985 165.09) */;
--wa-color-green-10: #032317 /* oklch(22.865% 0.0446 164.36) */;
--wa-color-green-05: #01160d /* oklch(17.827% 0.03566 164.38) */;
--wa-color-green: var(--wa-color-green-80);
--wa-color-green-key: 80;
--wa-color-blue-95: #ecf3ff /* oklch(96.238% 0.01777 261.34) */;
--wa-color-blue-90: #d9e7ff /* oklch(92.484% 0.03602 261.3) */;
--wa-color-blue-80: #abcaff /* oklch(83.49% 0.082 261.03) */;
--wa-color-blue-70: #83b0fe /* oklch(75.655% 0.12308 260.97) */;
--wa-color-blue-60: #5593fe /* oklch(67.328% 0.1713 260.55) */;
--wa-color-blue-50: #3d74d2 /* oklch(57.024% 0.15628 260.38) */;
--wa-color-blue-40: #2e589f /* oklch(46.829% 0.12443 260.23) */;
--wa-color-blue-30: #24457c /* oklch(39.571% 0.10098 260.03) */;
--wa-color-blue-20: #1b325b /* oklch(32.168% 0.07881 261.06) */;
--wa-color-blue-10: #101d35 /* oklch(23.292% 0.05008 261.75) */;
--wa-color-blue-05: #0a1222 /* oklch(18.375% 0.0352 263.19) */;
--wa-color-blue: var(--wa-color-blue-60);
--wa-color-cyan-95: #cafbfd /* oklch(95.491% 0.04984 199.43) */;
--wa-color-cyan-90: #97f5f8 /* oklch(91.286% 0.08952 198.32) */;
--wa-color-cyan-80: #3fdee7 /* oklch(82.5% 0.12703 200.7) */;
--wa-color-cyan-70: #00c0d4 /* oklch(73.886% 0.127 207.98) */;
--wa-color-cyan-60: #00a3bf /* oklch(65.895% 0.11686 215.74) */;
--wa-color-cyan-50: #007fa2 /* oklch(55.537% 0.10592 225.72) */;
--wa-color-cyan-40: #006082 /* oklch(45.89% 0.09291 231.37) */;
--wa-color-cyan-30: #014a6b /* oklch(38.746% 0.08372 236.7) */;
--wa-color-cyan-20: #003652 /* oklch(31.672% 0.07138 239.03) */;
--wa-color-cyan-10: #002034 /* oklch(23.245% 0.05366 240.46) */;
--wa-color-cyan-05: #001422 /* oklch(18.196% 0.041 239.02) */;
--wa-color-cyan: var(--wa-color-cyan-80);
--wa-color-cyan-key: 80;
--wa-color-indigo-95: #f5efff /* oklch(96.142% 0.02215 302.94) */;
--wa-color-indigo-90: #ede0ff /* oklch(92.64% 0.04374 304.51) */;
--wa-color-indigo-80: #d7bdff /* oklch(84.265% 0.09443 302.44) */;
--wa-color-indigo-70: #bf9efe /* oklch(76.541% 0.13786 298.94) */;
--wa-color-indigo-60: #a280fa /* oklch(68.571% 0.17519 294.14) */;
--wa-color-indigo-50: #7f5bec /* oklch(58.581% 0.20817 289.86) */;
--wa-color-indigo-40: #603dc8 /* oklch(48.557% 0.20262 287.93) */;
--wa-color-indigo-30: #4c2ba5 /* oklch(41.119% 0.18229 287.94) */;
--wa-color-indigo-20: #381d7f /* oklch(33.629% 0.15279 287.75) */;
--wa-color-indigo-10: #210e53 /* oklch(24.589% 0.11518 287.13) */;
--wa-color-indigo-05: #150739 /* oklch(19.258% 0.08946 287.81) */;
--wa-color-indigo: var(--wa-color-indigo-50);
--wa-color-blue-95: #e5f6ff /* oklch(96.324% 0.02167 230.54) */;
--wa-color-blue-90: #c7ebff /* oklch(92.061% 0.04647 232.28) */;
--wa-color-blue-80: #8ed2f9 /* oklch(83.243% 0.08774 234.44) */;
--wa-color-blue-70: #5db7f3 /* oklch(74.844% 0.12234 240.33) */;
--wa-color-blue-60: #309aee /* oklch(66.707% 0.15592 247.36) */;
--wa-color-blue-50: #0774df /* oklch(56.704% 0.18422 255.07) */;
--wa-color-blue-40: #0152c1 /* oklch(47.066% 0.187 259.59) */;
--wa-color-blue-30: #003ea2 /* oklch(40.154% 0.17138 260.8) */;
--wa-color-blue-20: #002b7f /* oklch(32.816% 0.14852 261.7) */;
--wa-color-blue-10: #001853 /* oklch(24.067% 0.11176 262.05) */;
--wa-color-blue-05: #000e39 /* oklch(18.796% 0.0868 261.98) */;
--wa-color-blue: var(--wa-color-blue-40);
--wa-color-blue-key: 40;
--wa-color-violet-95: #fbedfd /* oklch(96.172% 0.02584 321.94) */;
--wa-color-violet-90: #f7defa /* oklch(92.922% 0.04588 322.59) */;
--wa-color-violet-80: #eeb6f5 /* oklch(84.707% 0.10462 322.7) */;
--wa-color-violet-70: #e590f0 /* oklch(77.326% 0.15957 322.79) */;
--wa-color-violet-60: #d56ae2 /* oklch(69.373% 0.19861 323.18) */;
--wa-color-violet-50: #b547be /* oklch(59.084% 0.2006 324.63) */;
--wa-color-violet-40: #952598 /* oklch(48.958% 0.19474 326.91) */;
--wa-color-violet-30: #751d79 /* oklch(41.379% 0.16261 326.12) */;
--wa-color-violet-20: #561658 /* oklch(33.585% 0.12607 326.65) */;
--wa-color-violet-10: #340d36 /* oklch(24.532% 0.08615 325.79) */;
--wa-color-violet-05: #210822 /* oklch(19.065% 0.0603 326.11) */;
--wa-color-violet: var(--wa-color-violet-50);
--wa-color-indigo-95: #f0f2ff /* oklch(96.341% 0.0175 279.06) */;
--wa-color-indigo-90: #e1e4fe /* oklch(92.448% 0.03538 280.45) */;
--wa-color-indigo-80: #bfc6fe /* oklch(84.035% 0.07865 279.01) */;
--wa-color-indigo-70: #9fa8fc /* oklch(75.744% 0.12084 278.41) */;
--wa-color-indigo-60: #828bf6 /* oklch(67.774% 0.15666 277.72) */;
--wa-color-indigo-50: #6166e8 /* oklch(57.738% 0.19274 276.85) */;
--wa-color-indigo-40: #4743d0 /* oklch(48.143% 0.20939 276.34) */;
--wa-color-indigo-30: #372db3 /* oklch(41.009% 0.20044 276.19) */;
--wa-color-indigo-20: #291b90 /* oklch(33.874% 0.17803 276.48) */;
--wa-color-indigo-10: #170a61 /* oklch(24.844% 0.13871 276.05) */;
--wa-color-indigo-05: #0e0445 /* oklch(19.553% 0.11104 276.58) */;
--wa-color-indigo: var(--wa-color-indigo-40);
--wa-color-indigo-key: 40;
--wa-color-gray-95: #f1f2f4 /* oklch(96.095% 0.00288 264.54) */;
--wa-color-gray-90: #e2e5e8 /* oklch(92.046% 0.00521 247.88) */;
--wa-color-gray-80: #c2c9d0 /* oklch(83.255% 0.01246 247.98) */;
--wa-color-gray-70: #a6b0ba /* oklch(75.244% 0.01824 248.07) */;
--wa-color-gray-60: #8897a3 /* oklch(66.841% 0.02477 242.06) */;
--wa-color-gray-50: #657888 /* oklch(56.309% 0.0335 243.8) */;
--wa-color-gray-40: #435c6f /* oklch(46.235% 0.04323 241.6) */;
--wa-color-gray-30: #2d485d /* oklch(38.946% 0.04864 242.89) */;
--wa-color-gray-20: #18354a /* oklch(31.701% 0.05142 242.24) */;
--wa-color-gray-10: #012034 /* oklch(23.295% 0.05299 241.23) */;
--wa-color-gray-05: #001421 /* oklch(18.122% 0.03959 237.04) */;
--wa-color-purple-95: #fbefff /* oklch(96.613% 0.02465 317.72) */;
--wa-color-purple-90: #f7ddff /* oklch(92.895% 0.0531 318.2) */;
--wa-color-purple-80: #edb7ff /* oklch(85.165% 0.11316 317.53) */;
--wa-color-purple-70: #dc91fe /* oklch(77.161% 0.16803 314.62) */;
--wa-color-purple-60: #c670f2 /* oklch(69.273% 0.19845 312.9) */;
--wa-color-purple-50: #a34bda /* oklch(58.947% 0.21432 309.63) */;
--wa-color-purple-40: #802fb6 /* oklch(48.965% 0.20261 307.82) */;
--wa-color-purple-30: #661f96 /* oklch(41.488% 0.1815 307.02) */;
--wa-color-purple-20: #4c1372 /* oklch(33.753% 0.1509 306.93) */;
--wa-color-purple-10: #30074a /* oklch(24.849% 0.1139 307.37) */;
--wa-color-purple-05: #200333 /* oklch(19.47% 0.0902 307.77) */;
--wa-color-purple: var(--wa-color-purple-50);
--wa-color-purple-key: 50;
--wa-color-pink-95: #ffedf6 /* oklch(96.291% 0.02268 345.75) */;
--wa-color-pink-90: #feddee /* oklch(92.966% 0.04248 345.5) */;
--wa-color-pink-80: #feb2e3 /* oklch(84.978% 0.10743 340.7) */;
--wa-color-pink-70: #f688de /* oklch(77.315% 0.16826 335.79) */;
--wa-color-pink-60: #e560d6 /* oklch(69.598% 0.21242 332.2) */;
--wa-color-pink-50: #c234bf /* oklch(59.18% 0.23062 328.94) */;
--wa-color-pink-40: #9b189b /* oklch(49.224% 0.21096 328.21) */;
--wa-color-pink-30: #7c097e /* oklch(41.626% 0.18577 327.45) */;
--wa-color-pink-20: #5e0160 /* oklch(34.016% 0.15613 327.29) */;
--wa-color-pink-10: #3c003c /* oklch(24.992% 0.11486 328.36) */;
--wa-color-pink-05: #280029 /* oklch(19.507% 0.09014 327.31) */;
--wa-color-pink: var(--wa-color-pink-50);
--wa-color-pink-key: 50;
--wa-color-gray-95: #f2f2f2 /* oklch(96.115% 0 none) */;
--wa-color-gray-90: #e6e6e6 /* oklch(92.494% 0 none) */;
--wa-color-gray-80: #cacaca /* oklch(83.901% 0 none) */;
--wa-color-gray-70: #aeafaf /* oklch(75.321% 0.00114 197.13) */;
--wa-color-gray-60: #959595 /* oklch(66.984% 0 none) */;
--wa-color-gray-50: #757575 /* oklch(56.241% 0 none) */;
--wa-color-gray-40: #595959 /* oklch(46.4% 0 none) */;
--wa-color-gray-30: #454444 /* oklch(38.762% 0.00135 17.215) */;
--wa-color-gray-20: #323232 /* oklch(31.714% 0 none) */;
--wa-color-gray-10: #1d1d1d /* oklch(23.075% 0 none) */;
--wa-color-gray-05: #121212 /* oklch(18.22% 0 none) */;
--wa-color-gray: var(--wa-color-gray-40);
--wa-color-gray-key: 40;
}

View File

@@ -5,104 +5,140 @@
--wa-color-red-95: #ffefef /* oklch(96.475% 0.01735 17.458) */;
--wa-color-red-90: #fdd /* oklch(92.574% 0.03779 17.855) */;
--wa-color-red-80: #ffb7b7 /* oklch(84.652% 0.084 18.964) */;
--wa-color-red-70: #ff8e8e /* oklch(76.851% 0.13692 20.73) */;
--wa-color-red-60: #fd5f5f /* oklch(69.182% 0.19322 23.628) */;
--wa-color-red-50: #de3131 /* oklch(58.924% 0.20878 26.488) */;
--wa-color-red-40: #b40917 /* oklch(48.728% 0.19437 26.549) */;
--wa-color-red-30: #910000 /* oklch(41.235% 0.16921 29.234) */;
--wa-color-red-20: #6d0000 /* oklch(33.581% 0.1378 29.234) */;
--wa-color-red-10: #450000 /* oklch(24.517% 0.1006 29.234) */;
--wa-color-red-05: #300000 /* oklch(19.415% 0.07967 29.234) */;
--wa-color-red: var(--wa-color-red-50);
--wa-color-red-70: #ff8e90 /* oklch(76.896% 0.13677 19.629) */;
--wa-color-red-60: #f76563 /* oklch(69.077% 0.18003 23.786) */;
--wa-color-red-50: #d04442 /* oklch(58.513% 0.17687 25.152) */;
--wa-color-red-40: #a52c2b /* oklch(48.271% 0.15774 25.737) */;
--wa-color-red-30: #861d1c /* oklch(40.905% 0.1406 26.45) */;
--wa-color-red-20: #680d0e /* oklch(33.449% 0.1237 26.777) */;
--wa-color-red-10: #450002 /* oklch(24.549% 0.10017 27.414) */;
--wa-color-red-05: #2f0000 /* oklch(19.165% 0.07864 29.234) */;
--wa-color-red: var(--wa-color-red-60);
--wa-color-red-key: 60;
--wa-color-yellow-95: #fef3c1 /* oklch(96.062% 0.06532 97.08) */;
--wa-color-yellow-90: #fee682 /* oklch(92.403% 0.1233 96.206) */;
--wa-color-yellow-80: #fbc217 /* oklch(84.22% 0.16883 86.577) */;
--wa-color-yellow-70: #f39b00 /* oklch(76.07% 0.16522 69.58) */;
--wa-color-yellow-60: #e67700 /* oklch(68.349% 0.1693 54.965) */;
--wa-color-yellow-50: #b75d00 /* oklch(57.535% 0.1429 54.739) */;
--wa-color-yellow-40: #8c4600 /* oklch(47.31% 0.11712 55.012) */;
--wa-color-yellow-30: #6f3600 /* oklch(40.006% 0.09924 54.843) */;
--wa-color-yellow-20: #532600 /* oklch(32.518% 0.08157 53.927) */;
--wa-color-yellow-10: #341500 /* oklch(23.823% 0.06026 53.274) */;
--wa-color-yellow-05: #220c00 /* oklch(18.585% 0.04625 54.588) */;
--wa-color-yellow: var(--wa-color-yellow-60);
--wa-color-orange-95: oklch(96.406% 0.04001 53.476);
--wa-color-orange-90: oklch(92.395% 0.07984 53.06);
--wa-color-orange-80: oklch(84.389% 0.12224 47.981);
--wa-color-orange-70: oklch(76.55% 0.16521 42.512);
--wa-color-orange-60: #ea7237 /* oklch(68.444% 0.16501 44.349) */;
--wa-color-orange-50: #c0561a /* oklch(57.844% 0.15254 45.085) */;
--wa-color-orange-40: #963e05 /* oklch(47.639% 0.13153 45.898) */;
--wa-color-orange-30: oklch(40.376% 0.11554 45.517);
--wa-color-orange-20: oklch(32.94% 0.09927 45.913);
--wa-color-orange-10: oklch(24.083% 0.07743 46.027);
--wa-color-orange-05: oklch(18.817% 0.06098 48.455);
--wa-color-orange: var(--wa-color-orange-70);
--wa-color-orange-key: 70;
--wa-color-green-95: #e0fae4 /* oklch(96.026% 0.03986 149.82) */;
--wa-color-green-90: #baf4c2 /* oklch(91.456% 0.0897 148.52) */;
--wa-color-green-80: #73e085 /* oklch(81.974% 0.16188 147.55) */;
--wa-color-green-70: #46c65c /* oklch(73.25% 0.18463 146.42) */;
--wa-color-green-60: #33aa49 /* oklch(65.121% 0.17208 146.32) */;
--wa-color-green-50: #2a863c /* oklch(54.872% 0.13954 146.82) */;
--wa-color-green-40: #20662e /* oklch(45.254% 0.11199 147.1) */;
--wa-color-green-30: #194f24 /* oklch(38.029% 0.09087 147.43) */;
--wa-color-green-20: #123a1a /* oklch(31.086% 0.07165 147.58) */;
--wa-color-green-10: #0b2210 /* oklch(22.733% 0.04522 148.82) */;
--wa-color-green-05: #07150a /* oklch(17.871% 0.03021 149.94) */;
--wa-color-green: var(--wa-color-green-70);
--wa-color-yellow-95: #fff4c0 /* oklch(96.32% 0.0677 97.497) */;
--wa-color-yellow-90: #ffe579 /* oklch(92.176% 0.13122 96.089) */;
--wa-color-yellow-80: #ffbf18 /* oklch(84.069% 0.16897 83.446) */;
--wa-color-yellow-70: #f29c00 /* oklch(76.127% 0.16443 70.48) */;
--wa-color-yellow-60: #d08402 /* oklch(67.671% 0.14665 69.482) */;
--wa-color-yellow-50: #a56804 /* oklch(57.027% 0.1228 69.447) */;
--wa-color-yellow-40: #7d4f04 /* oklch(46.866% 0.09949 70.54) */;
--wa-color-yellow-30: #643d03 /* oklch(39.73% 0.08492 68.821) */;
--wa-color-yellow-20: #4a2c01 /* oklch(32.318% 0.06941 69.3) */;
--wa-color-yellow-10: #2d1901 /* oklch(23.512% 0.04963 68.776) */;
--wa-color-yellow-05: #1d0f00 /* oklch(18.392% 0.03939 71.947) */;
--wa-color-yellow: var(--wa-color-yellow-80);
--wa-color-yellow-key: 80;
--wa-color-teal-95: #d8fbf0 /* oklch(96.011% 0.03902 174.52) */;
--wa-color-teal-90: #a4f5dd /* oklch(91.124% 0.08611 173.73) */;
--wa-color-teal-80: #51e1b5 /* oklch(82.107% 0.13869 169.04) */;
--wa-color-teal-70: #1cc693 /* oklch(73.497% 0.14832 165.73) */;
--wa-color-teal-60: #0da97a /* oklch(65.179% 0.13571 164.57) */;
--wa-color-teal-50: #088660 /* oklch(55.001% 0.1145 164.58) */;
--wa-color-teal-40: #066549 /* oklch(45.014% 0.09185 165.65) */;
--wa-color-teal-30: #054f39 /* oklch(38.028% 0.07648 166.14) */;
--wa-color-teal-20: #043a2a /* oklch(31.057% 0.06074 167.25) */;
--wa-color-teal-10: #022319 /* oklch(22.882% 0.04342 168.87) */;
--wa-color-teal-05: #01160f /* oklch(17.9% 0.03352 169.84) */;
--wa-color-teal: var(--wa-color-teal-70);
--wa-color-green-95: #dbfaf0 /* oklch(96.006% 0.03481 174.15) */;
--wa-color-green-90: #b1f3de /* oklch(91.419% 0.0722 172.95) */;
--wa-color-green-80: #3fe2b2 /* oklch(81.853% 0.14907 168.5) */;
--wa-color-green-70: #29c596 /* oklch(73.487% 0.14194 166.89) */;
--wa-color-green-60: #19a87d /* oklch(65.107% 0.13007 166.01) */;
--wa-color-green-50: #0a8560 /* oklch(54.742% 0.11293 164.94) */;
--wa-color-green-40: #036648 /* oklch(45.239% 0.09474 164.51) */;
--wa-color-green-30: #015038 /* oklch(38.229% 0.08003 164.89) */;
--wa-color-green-20: #003a28 /* oklch(30.885% 0.06454 165.46) */;
--wa-color-green-10: #002317 /* oklch(22.712% 0.04718 165.92) */;
--wa-color-green-05: #00160d /* oklch(17.744% 0.03707 165.47) */;
--wa-color-green: var(--wa-color-green-80);
--wa-color-green-key: 80;
--wa-color-cyan-95: #dbf9fc /* oklch(96.146% 0.03142 203.97) */;
--wa-color-cyan-90: #a6f2fa /* oklch(91.438% 0.07533 204.25) */;
--wa-color-cyan-80: #51dcee /* oklch(82.7% 0.11986 207.31) */;
--wa-color-cyan-70: #2fbfd5 /* oklch(74.137% 0.11878 210.5) */;
--wa-color-cyan-60: #1fa3b9 /* oklch(65.917% 0.10909 212.4) */;
--wa-color-cyan-50: #118095 /* oklch(55.343% 0.09433 215.17) */;
--wa-color-cyan-40: #056273 /* oklch(45.726% 0.0795 215.29) */;
--wa-color-cyan-30: #014c5a /* oklch(38.37% 0.06757 215.53) */;
--wa-color-cyan-20: #003843 /* oklch(31.374% 0.0556 215.61) */;
--wa-color-cyan-10: #002129 /* oklch(22.851% 0.04085 217.17) */;
--wa-color-cyan-05: #00151a /* oklch(17.995% 0.03161 213.85) */;
--wa-color-cyan: var(--wa-color-cyan-80);
--wa-color-cyan-key: 80;
--wa-color-blue-95: #e7f5ff /* oklch(96.268% 0.02005 238.66) */;
--wa-color-blue-90: #ceeaff /* oklch(92.321% 0.04119 240.38) */;
--wa-color-blue-80: #94d0fe /* oklch(83.329% 0.08941 242.12) */;
--wa-color-blue-70: #60b5f9 /* oklch(74.8% 0.1287 244.9) */;
--wa-color-blue-60: #3299f0 /* oklch(66.644% 0.15866 248.64) */;
--wa-color-blue-50: #1a77cc /* oklch(56.301% 0.15449 251.61) */;
--wa-color-blue-40: #165a9b /* oklch(46.242% 0.12393 251.89) */;
--wa-color-blue-30: #11477a /* oklch(39.241% 0.10211 251.38) */;
--wa-color-blue-20: #0d3459 /* oklch(31.95% 0.07871 250.89) */;
--wa-color-blue-10: #071f35 /* oklch(23.365% 0.05197 249.19) */;
--wa-color-blue-05: #051321 /* oklch(18.208% 0.03553 248.92) */;
--wa-color-blue: var(--wa-color-blue-60);
--wa-color-blue-80: #93d0ff /* oklch(83.315% 0.09108 242.3) */;
--wa-color-blue-70: #5bb5fe /* oklch(74.85% 0.13654 245.56) */;
--wa-color-blue-60: #4a99e4 /* oklch(66.699% 0.13625 249.55) */;
--wa-color-blue-50: #3178c0 /* oklch(56.346% 0.13216 251.63) */;
--wa-color-blue-40: #235a96 /* oklch(46.273% 0.11344 253.15) */;
--wa-color-blue-30: #194777 /* oklch(39.272% 0.09544 252.36) */;
--wa-color-blue-20: #103359 /* oklch(31.787% 0.07886 253.15) */;
--wa-color-blue-10: #061e38 /* oklch(23.254% 0.05851 252.8) */;
--wa-color-blue-05: #031225 /* oklch(18.051% 0.04534 253.25) */;
--wa-color-blue: var(--wa-color-blue-70);
--wa-color-blue-key: 70;
--wa-color-indigo-95: #edf2ff /* oklch(96.116% 0.01824 269.09) */;
--wa-color-indigo-90: #dce5ff /* oklch(92.306% 0.03671 270.47) */;
--wa-color-indigo-80: #bac8ff /* oklch(84.108% 0.07867 273.5) */;
--wa-color-indigo-70: #96abff /* oklch(75.857% 0.12332 272.47) */;
--wa-color-indigo-60: #738efc /* oklch(67.734% 0.16478 271.06) */;
--wa-color-indigo-50: #486af1 /* oklch(57.714% 0.20605 268.43) */;
--wa-color-indigo-40: #354dc3 /* oklch(47.651% 0.18567 269.2) */;
--wa-color-indigo-30: #293c98 /* oklch(40.065% 0.15173 269.37) */;
--wa-color-indigo-20: #1e2c70 /* oklch(32.649% 0.11852 269.77) */;
--wa-color-indigo-10: #121a43 /* oklch(23.813% 0.07786 271) */;
--wa-color-indigo-05: #0b102a /* oklch(18.556% 0.05298 272.08) */;
--wa-color-indigo: var(--wa-color-indigo-50);
--wa-color-indigo-70: #95abff /* oklch(75.79% 0.12361 271.99) */;
--wa-color-indigo-60: #728dff /* oklch(67.658% 0.17035 271.19) */;
--wa-color-indigo-50: #516ed8 /* oklch(57.136% 0.16529 269.13) */;
--wa-color-indigo-40: #3851b3 /* oklch(47.273% 0.15818 268.81) */;
--wa-color-indigo-30: #2a3f8f /* oklch(39.968% 0.13476 268.52) */;
--wa-color-indigo-20: #1e2d6c /* oklch(32.512% 0.11114 269.2) */;
--wa-color-indigo-10: #101a45 /* oklch(23.864% 0.08185 269.36) */;
--wa-color-indigo-05: #080f2f /* oklch(18.545% 0.06467 269.54) */;
--wa-color-indigo: var(--wa-color-indigo-60);
--wa-color-indigo-key: 60;
--wa-color-violet-95: #f3f0ff /* oklch(96.181% 0.02019 295.19) */;
--wa-color-violet-90: #eae2ff /* oklch(92.785% 0.03984 298.23) */;
--wa-color-violet-80: #d1c0ff /* oklch(84.322% 0.08845 296.84) */;
--wa-color-violet-70: #b8a0fd /* oklch(76.245% 0.13251 294.93) */;
--wa-color-violet-60: #a080fb /* oklch(68.452% 0.17635 293.16) */;
--wa-color-violet-50: #7f57f5 /* oklch(58.672% 0.22391 289.27) */;
--wa-color-violet-40: #603dc7 /* oklch(48.471% 0.20143 288.1) */;
--wa-color-violet-30: #4c309c /* oklch(40.999% 0.16546 288.84) */;
--wa-color-violet-20: #372372 /* oklch(33.171% 0.12869 289.06) */;
--wa-color-violet-10: #211544 /* oklch(24.211% 0.08441 290.44) */;
--wa-color-violet-05: #150d2a /* oklch(18.859% 0.05661 292.88) */;
--wa-color-violet: var(--wa-color-violet-50);
--wa-color-purple-95: #f3f0ff /* oklch(96.181% 0.02019 295.19) */;
--wa-color-purple-90: #eae2ff /* oklch(92.785% 0.03984 298.23) */;
--wa-color-purple-80: #d1c0ff /* oklch(84.322% 0.08845 296.84) */;
--wa-color-purple-70: #b8a0fe /* oklch(76.293% 0.13384 294.76) */;
--wa-color-purple-60: #9e83f2 /* oklch(68.278% 0.16015 292.86) */;
--wa-color-purple-50: #7b65cb /* oklch(57.62% 0.15206 290.77) */;
--wa-color-purple-40: #5c4ba7 /* oklch(47.824% 0.14213 288.32) */;
--wa-color-purple-30: #493590 /* oklch(40.487% 0.14337 288.41) */;
--wa-color-purple-20: #371f7a /* oklch(33.309% 0.1443 287.97) */;
--wa-color-purple-10: #250161 /* oklch(25.284% 0.14353 287.73) */;
--wa-color-purple-05: #150047 /* oklch(19.858% 0.11707 283.68) */;
--wa-color-purple: var(--wa-color-purple-60);
--wa-color-purple-key: 60;
--wa-color-gray-95: #f1f2f4 /* oklch(96.095% 0.00288 264.54) */;
--wa-color-gray-90: #e3e5ea /* oklch(92.181% 0.00709 268.54) */;
--wa-color-gray-80: #c7cad4 /* oklch(83.966% 0.01424 272.66) */;
--wa-color-gray-70: #a9afbe /* oklch(75.397% 0.0225 268.37) */;
--wa-color-gray-60: #8d95a8 /* oklch(66.968% 0.02959 267.4) */;
--wa-color-pink-95: #f8effc /* oklch(96.287% 0.01965 315.67) */;
--wa-color-pink-90: #f4defb /* oklch(92.703% 0.04524 317.99) */;
--wa-color-pink-80: #edb8fa /* oklch(85.148% 0.10592 319.58) */;
--wa-color-pink-70: #e38ef8 /* oklch(77.16% 0.16992 319.48) */;
--wa-color-pink-60: #c576db /* oklch(68.843% 0.16426 318.62) */;
--wa-color-pink-50: #a258b5 /* oklch(58.347% 0.15642 319.13) */;
--wa-color-pink-40: #823b94 /* oklch(48.331% 0.15299 319.08) */;
--wa-color-pink-30: #6d247e /* oklch(41.177% 0.1547 319.42) */;
--wa-color-pink-20: #580a68 /* oklch(34.103% 0.15338 319.6) */;
--wa-color-pink-10: #370047 /* oklch(24.984% 0.12212 316.48) */;
--wa-color-pink-05: #240033 /* oklch(19.65% 0.09758 313.47) */;
--wa-color-pink: var(--wa-color-pink-70);
--wa-color-pink-key: 70;
--wa-color-gray-95: #f1f2f5 /* oklch(96.124% 0.00414 271.37) */;
--wa-color-gray-90: #e4e6eb /* oklch(92.484% 0.00709 268.54) */;
--wa-color-gray-80: #c5cad5 /* oklch(83.843% 0.01625 266.26) */;
--wa-color-gray-70: #a8afbf /* oklch(75.353% 0.02424 266.85) */;
--wa-color-gray-60: #8c95aa /* oklch(66.963% 0.0328 267.07) */;
--wa-color-gray-50: #6a7591 /* oklch(56.369% 0.04546 268.14) */;
--wa-color-gray-40: #4b5977 /* oklch(46.454% 0.05212 265.05) */;
--wa-color-gray-30: #344566 /* oklch(39.129% 0.06051 263) */;
--wa-color-gray-20: #1a3356 /* oklch(32.021% 0.07012 257.11) */;
--wa-color-gray-10: #0e1e35 /* oklch(23.442% 0.04969 257.55) */;
--wa-color-gray-05: #081220 /* oklch(18.072% 0.03272 256.72) */;
--wa-color-gray-40: #4a597a /* oklch(46.526% 0.05715 265.38) */;
--wa-color-gray-30: #384565 /* oklch(39.365% 0.05713 266.86) */;
--wa-color-gray-20: #26314f /* oklch(31.829% 0.05593 267.86) */;
--wa-color-gray-10: #131c38 /* oklch(23.46% 0.0555 268.48) */;
--wa-color-gray-05: #0a1127 /* oklch(18.485% 0.04617 268.36) */;
--wa-color-gray: var(--wa-color-gray-40);
--wa-color-gray-key: 40;
}

View File

@@ -2,96 +2,131 @@
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']),
.wa-palette-classic {
--wa-color-red-95: #feeeee /* oklch(96.175% 0.01736 17.459) */;
--wa-color-red-90: #fedede /* oklch(92.701% 0.03551 17.81) */;
--wa-color-red-80: #fdb8b8 /* oklch(84.646% 0.08038 18.878) */;
--wa-color-red-70: #fa9292 /* oklch(76.951% 0.12587 20.376) */;
--wa-color-red-60: #ee6c6c /* oklch(68.861% 0.16125 22.227) */;
--wa-color-red-50: #d43c3c /* oklch(58.269% 0.18916 25.263) */;
--wa-color-red-40: #ac1e1e /* oklch(48.229% 0.17712 27.066) */;
--wa-color-red-30: #640f0f /* oklch(32.787% 0.11797 26.73) */;
--wa-color-red-20: #631111 /* oklch(32.78% 0.11503 26.34) */;
--wa-color-red-10: #3b0d0d /* oklch(24.059% 0.07249 24.391) */;
--wa-color-red-05: #260606 /* oklch(18.371% 0.05502 24.325) */;
--wa-color-red-95: #fff0ef /* oklch(96.667% 0.01632 22.08) */;
--wa-color-red-90: #ffdedc /* oklch(92.735% 0.03679 21.966) */;
--wa-color-red-80: #ffb8b4 /* oklch(84.753% 0.08313 22.598) */;
--wa-color-red-70: #fd908e /* oklch(76.913% 0.13219 21.705) */;
--wa-color-red-60: #f46766 /* oklch(68.945% 0.17423 23.179) */;
--wa-color-red-50: #df2f2e /* oklch(58.922% 0.21141 26.911) */;
--wa-color-red-40: #b60000 /* oklch(48.747% 0.20003 29.234) */;
--wa-color-red-30: #910000 /* oklch(41.235% 0.16921 29.234) */;
--wa-color-red-20: #6d0000 /* oklch(33.581% 0.1378 29.234) */;
--wa-color-red-10: #450000 /* oklch(24.517% 0.1006 29.234) */;
--wa-color-red-05: #2f0000 /* oklch(19.165% 0.07864 29.234) */;
--wa-color-red: var(--wa-color-red-50);
--wa-color-red-key: 50;
--wa-color-yellow-95: #fef2c4 /* oklch(95.928% 0.0607 95.131) */;
--wa-color-yellow-90: #fde494 /* oklch(92.185% 0.10303 92.708) */;
--wa-color-yellow-80: #fbc129 /* oklch(84.088% 0.16334 85.185) */;
--wa-color-yellow-70: #f29c0b /* oklch(76.145% 0.1631 70.065) */;
--wa-color-yellow-60: #db7e13 /* oklch(68.058% 0.15388 60.67) */;
--wa-color-yellow-50: #af6005 /* oklch(56.918% 0.13324 58.984) */;
--wa-color-yellow-40: #904207 /* oklch(47.288% 0.12241 49.414) */;
--wa-color-yellow-30: #713406 /* oklch(40.01% 0.10093 50.35) */;
--wa-color-yellow-20: #532408 /* oklch(32.223% 0.07974 47.215) */;
--wa-color-yellow-10: #321606 /* oklch(23.771% 0.05225 48.671) */;
--wa-color-yellow-05: #1f0c01 /* oklch(18.046% 0.04102 55.818) */;
--wa-color-yellow: var(--wa-color-yellow-80);
--wa-color-orange-95: oklch(96.245% 0.04153 59.224);
--wa-color-orange-90: oklch(92.468% 0.07656 58.647);
--wa-color-orange-80: oklch(84.375% 0.11283 54.179);
--wa-color-orange-70: #fb945a /* oklch(76.369% 0.14454 48.621) */;
--wa-color-orange-60: #f26b31 /* oklch(68.509% 0.18046 41.503) */;
--wa-color-orange-50: #cf4812 /* oklch(58.288% 0.18026 38.576) */;
--wa-color-orange-40: oklch(48.175% 0.16316 38.526);
--wa-color-orange-30: oklch(40.779% 0.13925 37.899);
--wa-color-orange-20: oklch(33.129% 0.11288 38.58);
--wa-color-orange-10: oklch(24.259% 0.0831 38.502);
--wa-color-orange-05: oklch(18.969% 0.06527 38.137);
--wa-color-orange: var(--wa-color-orange-60);
--wa-color-orange-key: 60;
--wa-color-green-95: #e9f5ed /* oklch(95.897% 0.01651 156.91) */;
--wa-color-green-90: #cfedd9 /* oklch(91.883% 0.04142 156.31) */;
--wa-color-green-80: #a2d5b4 /* oklch(82.826% 0.07031 156.17) */;
--wa-color-green-70: #6cc08a /* oklch(73.992% 0.11416 154.35) */;
--wa-color-green-60: #38a961 /* oklch(65.365% 0.14702 151.78) */;
--wa-color-green-50: #178640 /* oklch(54.51% 0.14175 149.98) */;
--wa-color-green-40: #006823 /* oklch(45.073% 0.13335 147.17) */;
--wa-color-green-30: #0d5026 /* oklch(38.028% 0.09504 150.7) */;
--wa-color-green-20: #0c391d /* oklch(30.614% 0.06954 152.08) */;
--wa-color-green-10: #082213 /* oklch(22.658% 0.04352 155.31) */;
--wa-color-green-05: #02140a /* oklch(17.084% 0.03423 159.06) */;
--wa-color-yellow-95: #fef2bf /* oklch(95.823% 0.06674 96.369) */;
--wa-color-yellow-90: #fde588 /* oklch(92.2% 0.11633 95.327) */;
--wa-color-yellow-80: #f4c34e /* oklch(83.998% 0.14252 85.76) */;
--wa-color-yellow-70: #e9a010 /* oklch(75.825% 0.15689 75.537) */;
--wa-color-yellow-60: #de7c00 /* oklch(68.073% 0.15991 59.827) */;
--wa-color-yellow-50: #bc5908 /* oklch(57.654% 0.1491 50.241) */;
--wa-color-yellow-40: #934107 /* oklch(47.603% 0.12628 47.819) */;
--wa-color-yellow-30: #763104 /* oklch(40.324% 0.1093 46.564) */;
--wa-color-yellow-20: #572301 /* oklch(32.677% 0.08796 47.926) */;
--wa-color-yellow-10: #371300 /* oklch(24.001% 0.06559 47.77) */;
--wa-color-yellow-05: #250a00 /* oklch(18.773% 0.0519 47.039) */;
--wa-color-yellow: var(--wa-color-yellow-60);
--wa-color-yellow-key: 60;
--wa-color-green-95: #d4fce1 /* oklch(95.554% 0.05477 155.71) */;
--wa-color-green-90: #a4f8c2 /* oklch(91.11% 0.1107 155.35) */;
--wa-color-green-80: #6ae095 /* oklch(81.924% 0.15151 153.52) */;
--wa-color-green-70: #44c670 /* oklch(73.506% 0.16742 151) */;
--wa-color-green-60: #21ab52 /* oklch(65.151% 0.17062 149.59) */;
--wa-color-green-50: #0d873f /* oklch(54.644% 0.14593 150.18) */;
--wa-color-green-40: #166635 /* oklch(45.105% 0.10874 151.56) */;
--wa-color-green-30: #115029 /* oklch(38.182% 0.09049 151.63) */;
--wa-color-green-20: #0a3a1c /* oklch(30.854% 0.07315 151.48) */;
--wa-color-green-10: #04230f /* oklch(22.692% 0.05324 151.92) */;
--wa-color-green-05: #021608 /* oklch(17.756% 0.04076 152.68) */;
--wa-color-green: var(--wa-color-green-60);
--wa-color-green-key: 60;
--wa-color-teal-95: #d0fbf3 /* oklch(95.575% 0.04532 182.8) */;
--wa-color-teal-90: #a9f4e8 /* oklch(91.479% 0.07585 183.56) */;
--wa-color-teal-80: #5bdecd /* oklch(82.349% 0.1175 183.5) */;
--wa-color-teal-70: #36c2b3 /* oklch(73.796% 0.11779 184.4) */;
--wa-color-teal-60: #12a797 /* oklch(65.522% 0.11415 182.81) */;
--wa-color-teal-50: #0b8278 /* oklch(54.658% 0.09409 185.3) */;
--wa-color-teal-40: #09635b /* oklch(45.055% 0.07686 185.06) */;
--wa-color-teal-30: #0a4e49 /* oklch(38.363% 0.06313 187.15) */;
--wa-color-teal-20: #0a3835 /* oklch(30.997% 0.04799 188.56) */;
--wa-color-teal-10: #082120 /* oklch(22.8% 0.03074 191.62) */;
--wa-color-teal-05: #021413 /* oklch(17.442% 0.02643 190.53) */;
--wa-color-teal: var(--wa-color-teal-70);
--wa-color-cyan-95: #d8fafc /* oklch(96.149% 0.03524 200.93) */;
--wa-color-cyan-90: #a3f2f7 /* oklch(91.187% 0.07744 200.93) */;
--wa-color-cyan-80: #67dbe2 /* oklch(82.721% 0.1051 200.68) */;
--wa-color-cyan-70: #29c0ca /* oklch(73.859% 0.11825 201.93) */;
--wa-color-cyan-60: #0ca4ae /* oklch(65.523% 0.10997 202.61) */;
--wa-color-cyan-50: #0c828c /* oklch(55.424% 0.09237 204.53) */;
--wa-color-cyan-40: #0a626b /* oklch(45.428% 0.07504 206.17) */;
--wa-color-cyan-30: #084d55 /* oklch(38.542% 0.06312 207.36) */;
--wa-color-cyan-20: #06383f /* oklch(31.335% 0.05062 209.3) */;
--wa-color-cyan-10: #002127 /* oklch(22.739% 0.03961 211.94) */;
--wa-color-cyan-05: #00151b /* oklch(18.055% 0.03231 217.31) */;
--wa-color-cyan: var(--wa-color-cyan-70);
--wa-color-cyan-key: 70;
--wa-color-blue-95: #e5f4fe /* oklch(95.896% 0.02085 236.75) */;
--wa-color-blue-90: #c8ebfd /* oklch(92.061% 0.04406 230.21) */;
--wa-color-blue-80: #80d4fc /* oklch(83.114% 0.09915 230.17) */;
--wa-color-blue-70: #37bbf5 /* oklch(74.731% 0.13762 232.44) */;
--wa-color-blue-60: #0d9ee0 /* oklch(66.354% 0.14388 237.59) */;
--wa-color-blue-50: #027ab9 /* oklch(55.557% 0.13184 242.23) */;
--wa-color-blue-40: #015d8d /* oklch(45.762% 0.10698 241.38) */;
--wa-color-blue-30: #024970 /* oklch(38.805% 0.0903 241.59) */;
--wa-color-blue-20: #04354f /* oklch(31.306% 0.06703 238.73) */;
--wa-color-blue-10: #05202f /* oklch(23.154% 0.04349 236.83) */;
--wa-color-blue-05: #04121b /* oklch(17.393% 0.0282 236.81) */;
--wa-color-blue-95: #e2f6ff /* oklch(96.112% 0.0243 226.47) */;
--wa-color-blue-90: #c0ecff /* oklch(91.822% 0.0523 226) */;
--wa-color-blue-80: #7fd4fc /* oklch(83.068% 0.09979 229.91) */;
--wa-color-blue-70: #48b9f4 /* oklch(74.644% 0.13162 235.42) */;
--wa-color-blue-60: #1f9de2 /* oklch(66.419% 0.14398 240.02) */;
--wa-color-blue-50: #007bbc /* oklch(55.956% 0.1346 242.72) */;
--wa-color-blue-40: #005d93 /* oklch(46.121% 0.11438 244.28) */;
--wa-color-blue-30: #004975 /* oklch(39.093% 0.09705 244.33) */;
--wa-color-blue-20: #003558 /* oklch(31.8% 0.08026 245.13) */;
--wa-color-blue-10: #001f36 /* oklch(23.093% 0.05763 244.59) */;
--wa-color-blue-05: #001325 /* oklch(18.113% 0.04675 246.17) */;
--wa-color-blue: var(--wa-color-blue-60);
--wa-color-blue-key: 60;
--wa-color-indigo-95: #eef2ff /* oklch(96.191% 0.01793 272.31) */;
--wa-color-indigo-90: #dee5fd /* oklch(92.388% 0.03348 272.78) */;
--wa-color-indigo-80: #bec8f2 /* oklch(83.901% 0.05981 274.54) */;
--wa-color-indigo-70: #9dabf0 /* oklch(75.639% 0.10065 274.93) */;
--wa-color-indigo-60: #818cf7 /* oklch(67.953% 0.15693 276.94) */;
--wa-color-indigo-50: #6063eb /* oklch(57.366% 0.20061 277.06) */;
--wa-color-indigo-40: #4941d3 /* oklch(48.251% 0.21482 277.09) */;
--wa-color-indigo-30: #3930ad /* oklch(40.969% 0.18954 277.17) */;
--wa-color-indigo-20: #29247a /* oklch(32.82% 0.14032 277.91) */;
--wa-color-indigo-10: #191843 /* oklch(23.918% 0.07895 279.64) */;
--wa-color-indigo-05: #0f0e26 /* oklch(18.064% 0.04799 282.35) */;
--wa-color-indigo-95: #eff2ff /* oklch(96.265% 0.01769 275.64) */;
--wa-color-indigo-90: #e0e5ff /* oklch(92.602% 0.03569 277.03) */;
--wa-color-indigo-80: #bec7ff /* oklch(84.198% 0.07895 277.45) */;
--wa-color-indigo-70: #9da9fc /* oklch(75.797% 0.11994 276.85) */;
--wa-color-indigo-60: #808bf8 /* oklch(67.752% 0.15983 276.9) */;
--wa-color-indigo-50: #6163f0 /* oklch(57.814% 0.20686 277.14) */;
--wa-color-indigo-40: #4a41d4 /* oklch(48.413% 0.21583 277.35) */;
--wa-color-indigo-30: #3b369d /* oklch(40.562% 0.16149 278.54) */;
--wa-color-indigo-20: #2b2872 /* oklch(32.937% 0.12339 278.99) */;
--wa-color-indigo-10: #191748 /* oklch(24.174% 0.08853 279.28) */;
--wa-color-indigo-05: #0e0d31 /* oklch(18.772% 0.06933 278.6) */;
--wa-color-indigo: var(--wa-color-indigo-40);
--wa-color-indigo-key: 40;
--wa-color-violet-95: #f7efff /* oklch(96.298% 0.02281 308.2) */;
--wa-color-violet-90: #efe0ff /* oklch(92.801% 0.0444 307.25) */;
--wa-color-violet-80: #dcbdfe /* oklch(84.655% 0.09457 306.19) */;
--wa-color-violet-70: #cb9afd /* oklch(76.897% 0.14583 306.03) */;
--wa-color-violet-60: #b976f9 /* oklch(69.137% 0.19284 305.56) */;
--wa-color-violet-50: #9d46ec /* oklch(58.928% 0.237 303.82) */;
--wa-color-violet-40: #7d22cc /* oklch(49.256% 0.23487 302) */;
--wa-color-violet-30: #631f9c /* oklch(41.597% 0.18675 303.6) */;
--wa-color-violet-20: #48176e /* oklch(33.244% 0.14133 305.45) */;
--wa-color-violet-10: #2e054e /* oklch(24.726% 0.11996 303.55) */;
--wa-color-violet-05: #1d0331 /* oklch(18.767% 0.08707 305.63) */;
--wa-color-violet: var(--wa-color-violet-50);
--wa-color-purple-95: #f6f0ff /* oklch(96.412% 0.02086 304.04) */;
--wa-color-purple-90: #eee0ff /* oklch(92.72% 0.04406 305.89) */;
--wa-color-purple-80: #dcbdff /* oklch(84.694% 0.09583 305.85) */;
--wa-color-purple-70: #ca99ff /* oklch(76.728% 0.14961 305.27) */;
--wa-color-purple-60: #b874ff /* oklch(69.085% 0.2024 304.19) */;
--wa-color-purple-50: #9f46ee /* oklch(59.304% 0.23941 304.1) */;
--wa-color-purple-40: #7e2ac2 /* oklch(49.181% 0.21892 304.24) */;
--wa-color-purple-30: #632198 /* oklch(41.448% 0.18071 304.64) */;
--wa-color-purple-20: #4a1574 /* oklch(33.839% 0.15043 304.56) */;
--wa-color-purple-10: #2e094b /* oklch(24.855% 0.11212 304.52) */;
--wa-color-purple-05: #1e0433 /* oklch(19.319% 0.0877 304.85) */;
--wa-color-purple: var(--wa-color-purple-50);
--wa-color-purple-key: 50;
--wa-color-pink-95: #fdeff7 /* oklch(96.539% 0.01831 342.02) */;
--wa-color-pink-90: #fcdcee /* oklch(92.607% 0.04228 343.07) */;
--wa-color-pink-80: #fab6db /* oklch(84.992% 0.09131 345.4) */;
--wa-color-pink-70: #f78cc4 /* oklch(77.072% 0.14455 348.37) */;
--wa-color-pink-60: #f160a9 /* oklch(69.418% 0.19234 351.99) */;
--wa-color-pink-50: #db2878 /* oklch(59.255% 0.21755 0.28257) */;
--wa-color-pink-40: #ab1854 /* oklch(48.723% 0.18199 3.8244) */;
--wa-color-pink-30: #851844 /* oklch(41.188% 0.14622 2.4184) */;
--wa-color-pink-20: #640e30 /* oklch(33.442% 0.12022 3.3857) */;
--wa-color-pink-10: #43001a /* oklch(24.613% 0.09885 5.3211) */;
--wa-color-pink-05: #2f000b /* oklch(19.457% 0.07776 12.211) */;
--wa-color-pink: var(--wa-color-pink-50);
--wa-color-pink-key: 50;
--wa-color-gray-95: #f2f2f3 /* oklch(96.143% 0.00133 286.37) */;
--wa-color-gray-90: #e5e5e8 /* oklch(92.276% 0.00403 286.32) */;
@@ -105,4 +140,5 @@
--wa-color-gray-10: #1d1d20 /* oklch(23.201% 0.00571 285.95) */;
--wa-color-gray-05: #101113 /* oklch(17.739% 0.00442 264.46) */;
--wa-color-gray: var(--wa-color-gray-40);
--wa-color-gray-key: 40;
}

View File

@@ -2,96 +2,131 @@
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']),
.wa-palette-default {
--wa-color-red-95: #ffefef /* oklch(96.475% 0.01735 17.458) */;
--wa-color-red-90: #ffdddc /* oklch(92.547% 0.03785 19.865) */;
--wa-color-red-80: #ffb7b6 /* oklch(84.626% 0.08407 19.872) */;
--wa-color-red-70: #fc9090 /* oklch(76.834% 0.13083 20.537) */;
--wa-color-red-60: #f2676c /* oklch(68.768% 0.17146 20.596) */;
--wa-color-red-50: #de2d44 /* oklch(58.887% 0.20998 20.414) */;
--wa-color-red-40: #b11036 /* oklch(48.766% 0.18763 17.443) */;
--wa-color-red-30: #861a2f /* oklch(40.968% 0.14194 15.902) */;
--wa-color-red-20: #641122 /* oklch(33.29% 0.11505 15.196) */;
--wa-color-red-10: #400712 /* oklch(24.506% 0.08542 15.881) */;
--wa-color-red-05: #2a030a /* oklch(18.798% 0.06593 14.104) */;
--wa-color-red-95: #fff0ef /* oklch(96.667% 0.01632 22.08) */;
--wa-color-red-90: #ffdedc /* oklch(92.735% 0.03679 21.966) */;
--wa-color-red-80: #ffb8b6 /* oklch(84.803% 0.08289 20.771) */;
--wa-color-red-70: #fd8f90 /* oklch(76.801% 0.13322 20.052) */;
--wa-color-red-60: #f3676c /* oklch(68.914% 0.17256 20.646) */;
--wa-color-red-50: #dc3146 /* oklch(58.857% 0.20512 20.223) */;
--wa-color-red-40: #b30532 /* oklch(48.737% 0.19311 18.413) */;
--wa-color-red-30: #8a132c /* oklch(41.17% 0.1512 16.771) */;
--wa-color-red-20: #631323 /* oklch(33.297% 0.11208 14.847) */;
--wa-color-red-10: #3e0913 /* oklch(24.329% 0.08074 15.207) */;
--wa-color-red-05: #2a040b /* oklch(19.016% 0.06394 13.71) */;
--wa-color-red: var(--wa-color-red-50);
--wa-color-red-key: 50;
--wa-color-yellow-95: #fdf3ba /* oklch(95.838% 0.07362 99.275) */;
--wa-color-yellow-90: #fee590 /* oklch(92.407% 0.10827 93.577) */;
--wa-color-yellow-80: #fcc041 /* oklch(84.136% 0.1524 82.087) */;
--wa-color-yellow-70: #f39b00 /* oklch(76.07% 0.16522 69.58) */;
--wa-color-yellow-60: #e07b00 /* oklch(68.175% 0.16203 58.675) */;
--wa-color-yellow-50: #bb5a00 /* oklch(57.639% 0.14858 51.823) */;
--wa-color-yellow-40: #924200 /* oklch(47.573% 0.12584 49.96) */;
--wa-color-yellow-30: #743200 /* oklch(40.128% 0.10738 49.167) */;
--wa-color-yellow-20: #572300 /* oklch(32.668% 0.08836 48.446) */;
--wa-color-yellow-10: #361300 /* oklch(23.81% 0.06438 48.467) */;
--wa-color-yellow-05: #240b00 /* oklch(18.767% 0.04963 49.978) */;
--wa-color-yellow: var(--wa-color-yellow-70);
--wa-color-orange-95: oklch(96.494% 0.0335 57.914);
--wa-color-orange-90: oklch(92.556% 0.06963 56.631);
--wa-color-orange-80: oklch(84.494% 0.12276 53.381);
--wa-color-orange-70: oklch(76.375% 0.17194 46.091);
--wa-color-orange-60: #eb713f /* oklch(68.398% 0.16422 41.446) */;
--wa-color-orange-50: #cb4b1d /* oklch(58.153% 0.17174 38.404) */;
--wa-color-orange-40: #a2310c /* oklch(48.028% 0.15488 36.538) */;
--wa-color-orange-30: #7f2810 /* oklch(40.591% 0.12506 35.663) */;
--wa-color-orange-20: #5d1d0e /* oklch(32.908% 0.09683 34.387) */;
--wa-color-orange-10: #3a1005 /* oklch(24.088% 0.06954 35.613) */;
--wa-color-orange-05: #270803 /* oklch(18.801% 0.05509 34.149) */;
--wa-color-orange: var(--wa-color-orange-70);
--wa-color-orange-key: 70;
--wa-color-green-95: #e2f9e2 /* oklch(95.91% 0.03884 145.26) */;
--wa-color-yellow-95: #fef3cd /* oklch(96.322% 0.05069 93.748) */;
--wa-color-yellow-90: #ffe495 /* oklch(92.377% 0.10246 91.296) */;
--wa-color-yellow-80: #fac22b /* oklch(84.185% 0.16263 85.991) */;
--wa-color-yellow-70: #ef9d00 /* oklch(75.949% 0.16251 72.13) */;
--wa-color-yellow-60: #da7e00 /* oklch(67.883% 0.15587 62.246) */;
--wa-color-yellow-50: #b45f04 /* oklch(57.449% 0.13836 56.585) */;
--wa-color-yellow-40: #8c4602 /* oklch(47.319% 0.11666 54.663) */;
--wa-color-yellow-30: #6f3601 /* oklch(40.012% 0.09892 54.555) */;
--wa-color-yellow-20: #532600 /* oklch(32.518% 0.08157 53.927) */;
--wa-color-yellow-10: #331600 /* oklch(23.846% 0.05834 56.02) */;
--wa-color-yellow-05: #220c00 /* oklch(18.585% 0.04625 54.588) */;
--wa-color-yellow: var(--wa-color-yellow-80);
--wa-color-yellow-key: 80;
--wa-color-green-95: #e3f9e3 /* oklch(96.006% 0.03715 145.28) */;
--wa-color-green-90: #c2f2c1 /* oklch(91.494% 0.08233 144.35) */;
--wa-color-green-80: #92da97 /* oklch(82.37% 0.11765 146.06) */;
--wa-color-green-80: #93da98 /* oklch(82.445% 0.11601 146.11) */;
--wa-color-green-70: #5dc36f /* oklch(73.554% 0.15308 147.59) */;
--wa-color-green-60: #00ac49 /* oklch(64.982% 0.18414 148.83) */;
--wa-color-green-50: #008825 /* oklch(54.493% 0.16976 145.33) */;
--wa-color-green-40: #006800 /* oklch(44.821% 0.15252 142.5) */;
--wa-color-green-30: #005300 /* oklch(38.319% 0.13039 142.5) */;
--wa-color-green-20: #003c00 /* oklch(30.861% 0.10501 142.5) */;
--wa-color-green-10: #002400 /* oklch(22.556% 0.07675 142.5) */;
--wa-color-green-05: #001700 /* oklch(17.73% 0.06033 142.5) */;
--wa-color-green-50: #00883c /* oklch(54.765% 0.15165 149.77) */;
--wa-color-green-40: #036730 /* oklch(45.004% 0.11963 151.06) */;
--wa-color-green-30: #0a5027 /* oklch(37.988% 0.09487 151.62) */;
--wa-color-green-20: #0a3a1d /* oklch(30.876% 0.07202 152.23) */;
--wa-color-green-10: #052310 /* oklch(22.767% 0.05128 152.45) */;
--wa-color-green-05: #031608 /* oklch(17.84% 0.03957 151.36) */;
--wa-color-green: var(--wa-color-green-60);
--wa-color-green-key: 60;
--wa-color-teal-95: #e3f7f5 /* oklch(96.09% 0.02114 189.57) */;
--wa-color-teal-90: #c6eeeb /* oklch(91.997% 0.04158 190.86) */;
--wa-color-teal-80: #81d9d3 /* oklch(82.83% 0.08563 190.03) */;
--wa-color-teal-70: #34c2b9 /* oklch(73.949% 0.11679 188.57) */;
--wa-color-teal-60: #10a69d /* oklch(65.445% 0.11161 187.92) */;
--wa-color-teal-50: #00837c /* oklch(54.971% 0.09552 188.22) */;
--wa-color-teal-40: #00645e /* oklch(45.331% 0.07895 187.65) */;
--wa-color-teal-30: #004e49 /* oklch(38.182% 0.06656 187.42) */;
--wa-color-teal-20: #003935 /* oklch(31.03% 0.05415 187.16) */;
--wa-color-teal-10: #002220 /* oklch(22.721% 0.03935 189.11) */;
--wa-color-teal-05: #001513 /* oklch(17.625% 0.03077 187.05) */;
--wa-color-teal: var(--wa-color-teal-70);
--wa-color-cyan-95: #e3f6fb /* oklch(96.063% 0.02111 215.26) */;
--wa-color-cyan-90: #c5ecf7 /* oklch(91.881% 0.04314 216.7) */;
--wa-color-cyan-80: #7fd6ec /* oklch(82.906% 0.08934 215.86) */;
--wa-color-cyan-70: #2fbedc /* oklch(74.18% 0.12169 215.86) */;
--wa-color-cyan-60: #00a3c0 /* oklch(65.939% 0.11738 216.42) */;
--wa-color-cyan-50: #078098 /* oklch(55.379% 0.09774 217.32) */;
--wa-color-cyan-40: #026274 /* oklch(45.735% 0.08074 216.18) */;
--wa-color-cyan-30: #014c5b /* oklch(38.419% 0.06817 216.88) */;
--wa-color-cyan-20: #003844 /* oklch(31.427% 0.05624 217.32) */;
--wa-color-cyan-10: #002129 /* oklch(22.851% 0.04085 217.17) */;
--wa-color-cyan-05: #00151b /* oklch(18.055% 0.03231 217.31) */;
--wa-color-cyan: var(--wa-color-cyan-70);
--wa-color-cyan-key: 70;
--wa-color-blue-95: #ebf4ff /* oklch(96.361% 0.01763 253.34) */;
--wa-color-blue-90: #d4e7ff /* oklch(92.129% 0.03859 254.29) */;
--wa-color-blue-80: #a6ccff /* oklch(83.582% 0.08269 255.9) */;
--wa-color-blue-70: #77b1ff /* oklch(75.268% 0.12855 256.14) */;
--wa-color-blue-60: #4895fd /* oklch(67.184% 0.17212 256.8) */;
--wa-color-blue-50: #0070ef /* oklch(56.997% 0.20953 257.84) */;
--wa-color-blue-40: #0055b8 /* oklch(46.934% 0.17173 257.73) */;
--wa-color-blue-30: #004390 /* oklch(39.638% 0.1408 257.02) */;
--wa-color-blue-20: #00306c /* oklch(32.102% 0.11523 257.27) */;
--wa-color-blue-10: #001c45 /* oklch(23.545% 0.08493 257.39) */;
--wa-color-blue-05: #00112f /* oklch(18.443% 0.06618 257.27) */;
--wa-color-blue-95: #e8f3ff /* oklch(95.944% 0.01996 250.38) */;
--wa-color-blue-90: #d1e8ff /* oklch(92.121% 0.03985 248.26) */;
--wa-color-blue-80: #9fceff /* oklch(83.572% 0.08502 249.92) */;
--wa-color-blue-70: #6eb3ff /* oklch(75.256% 0.1308 252.03) */;
--wa-color-blue-60: #3e96ff /* oklch(67.196% 0.17661 254.97) */;
--wa-color-blue-50: #0071ec /* oklch(56.972% 0.20461 257.29) */;
--wa-color-blue-40: #0053c0 /* oklch(47.175% 0.1846 259.19) */;
--wa-color-blue-30: #003f9c /* oklch(39.805% 0.16217 259.98) */;
--wa-color-blue-20: #002d77 /* oklch(32.436% 0.1349 260.35) */;
--wa-color-blue-10: #001a4e /* oklch(23.965% 0.10161 260.68) */;
--wa-color-blue-05: #000f35 /* oklch(18.565% 0.07904 260.75) */;
--wa-color-blue: var(--wa-color-blue-50);
--wa-color-blue-key: 50;
--wa-color-indigo-95: #f0f2fe /* oklch(96.311% 0.01617 278.51) */;
--wa-color-indigo-90: #e2e4fc /* oklch(92.459% 0.03255 281.95) */;
--wa-color-indigo-80: #c2c6f8 /* oklch(84.027% 0.06987 281.2) */;
--wa-color-indigo-70: #a5a9f2 /* oklch(75.915% 0.10451 281.18) */;
--wa-color-indigo-60: #8a8beb /* oklch(67.735% 0.14003 281.54) */;
--wa-color-indigo-50: #6b65e2 /* oklch(57.839% 0.18419 281.11) */;
--wa-color-indigo-40: #5246c1 /* oklch(48.113% 0.18471 281.3) */;
--wa-color-indigo-30: #412eaa /* oklch(41.007% 0.18636 281.32) */;
--wa-color-indigo-20: #321393 /* oklch(34.125% 0.1858 281.27) */;
--wa-color-indigo-10: #1c006a /* oklch(25.437% 0.15565 278.46) */;
--wa-color-indigo-05: #130049 /* oklch(19.925% 0.11977 280.89) */;
--wa-color-indigo: var(--wa-color-indigo-40);
--wa-color-indigo-95: #f0f2ff /* oklch(96.341% 0.0175 279.06) */;
--wa-color-indigo-90: #dfe5ff /* oklch(92.527% 0.0359 275.35) */;
--wa-color-indigo-80: #bcc7ff /* oklch(84.053% 0.07938 275.91) */;
--wa-color-indigo-70: #9da9ff /* oklch(75.941% 0.12411 276.95) */;
--wa-color-indigo-60: #808aff /* oklch(67.977% 0.17065 277.16) */;
--wa-color-indigo-50: #6163f2 /* oklch(57.967% 0.20943 277.04) */;
--wa-color-indigo-40: #4945cb /* oklch(48.145% 0.20042 277.08) */;
--wa-color-indigo-30: #3933a7 /* oklch(40.844% 0.17864 277.26) */;
--wa-color-indigo-20: #292381 /* oklch(33.362% 0.15096 277.21) */;
--wa-color-indigo-10: #181255 /* oklch(24.534% 0.11483 277.73) */;
--wa-color-indigo-05: #0d0a3a /* oklch(19.092% 0.08825 276.76) */;
--wa-color-indigo: var(--wa-color-indigo-50);
--wa-color-indigo-key: 50;
--wa-color-violet-95: #f9effd /* oklch(96.395% 0.02131 316.46) */;
--wa-color-violet-90: #f4defb /* oklch(92.703% 0.04524 317.99) */;
--wa-color-violet-80: #e7baf7 /* oklch(84.83% 0.09589 317.09) */;
--wa-color-violet-70: #d996ef /* oklch(76.972% 0.14185 317.52) */;
--wa-color-violet-60: #c674e1 /* oklch(68.954% 0.1743 317.29) */;
--wa-color-violet-50: #a94dc6 /* oklch(58.676% 0.19389 317.27) */;
--wa-color-violet-40: #8732a1 /* oklch(48.724% 0.18099 317.24) */;
--wa-color-violet-30: #6d2283 /* oklch(41.31% 0.16202 317.42) */;
--wa-color-violet-20: #521564 /* oklch(33.69% 0.13677 317.22) */;
--wa-color-violet-10: #330940 /* oklch(24.654% 0.10189 316.7) */;
--wa-color-violet-05: #22042b /* oklch(19.232% 0.08005 317.42) */;
--wa-color-violet: var(--wa-color-violet-50);
--wa-color-purple-95: #f7f0ff /* oklch(96.49% 0.02119 306.84) */;
--wa-color-purple-90: #eedfff /* oklch(92.531% 0.04569 306.6) */;
--wa-color-purple-80: #ddbdff /* oklch(84.781% 0.09615 306.52) */;
--wa-color-purple-70: #ca99ff /* oklch(76.728% 0.14961 305.27) */;
--wa-color-purple-60: #b678f5 /* oklch(68.906% 0.1844 304.96) */;
--wa-color-purple-50: #9951db /* oklch(58.603% 0.20465 304.87) */;
--wa-color-purple-40: #7936b3 /* oklch(48.641% 0.18949 304.79) */;
--wa-color-purple-30: #612692 /* oklch(41.23% 0.16836 304.92) */;
--wa-color-purple-20: #491870 /* oklch(33.663% 0.14258 305.12) */;
--wa-color-purple-10: #2d0b48 /* oklch(24.637% 0.10612 304.95) */;
--wa-color-purple-05: #1e0532 /* oklch(19.393% 0.08461 305.26) */;
--wa-color-purple: var(--wa-color-purple-50);
--wa-color-purple-key: 50;
--wa-color-pink-95: #feeff9 /* oklch(96.676% 0.02074 337.69) */;
--wa-color-pink-90: #feddf0 /* oklch(93.026% 0.04388 342.45) */;
--wa-color-pink-80: #fcb5d8 /* oklch(84.928% 0.09304 348.21) */;
--wa-color-pink-70: #f78dbf /* oklch(77.058% 0.14016 351.19) */;
--wa-color-pink-60: #e66ba3 /* oklch(69.067% 0.16347 353.69) */;
--wa-color-pink-50: #c84382 /* oklch(58.707% 0.17826 354.82) */;
--wa-color-pink-40: #9e2a6c /* oklch(48.603% 0.16439 350.08) */;
--wa-color-pink-30: #7d1e58 /* oklch(41.017% 0.14211 347.77) */;
--wa-color-pink-20: #5e1342 /* oklch(33.442% 0.11808 347.01) */;
--wa-color-pink-10: #3c0828 /* oklch(24.601% 0.08768 347.8) */;
--wa-color-pink-05: #28041a /* oklch(19.199% 0.06799 346.97) */;
--wa-color-pink: var(--wa-color-pink-50);
--wa-color-pink-key: 50;
--wa-color-gray-95: #f1f2f3 /* oklch(96.067% 0.00172 247.84) */;
--wa-color-gray-90: #e4e5e9 /* oklch(92.228% 0.0055 274.96) */;
@@ -105,4 +140,5 @@
--wa-color-gray-10: #1b1d26 /* oklch(23.277% 0.01762 275.14) */;
--wa-color-gray-05: #101219 /* oklch(18.342% 0.01472 272.42) */;
--wa-color-gray: var(--wa-color-gray-40);
--wa-color-gray-key: 40;
}

Some files were not shown because too many files have changed in this diff Show More