Compare commits

..

18 Commits

Author SHA1 Message Date
Lea Verou
c77acdbcd5 Delete clamped-colors.njk
No longer necessary
2025-02-20 14:35:49 -05:00
Lea Verou
d574aef103 Docs 2025-02-20 14:35:30 -05:00
Lea Verou
0fd77ca80a Prettier 2025-02-20 13:19:12 -05:00
Lea Verou
aaffb4d221 Clamped tokens for all hues, semantic scales
Also renamed:
- `-if-[gte|lt]-*` tokens to just `-[gte|lt]-*`
- `-{tint}-{min|max}` -> `-{min|max}-{tint}`

Actually import base.css

Add semantic scales
2025-02-20 13:18:04 -05:00
Lea Verou
79c9708a14 Remove superfluous import 2025-02-20 13:18:04 -05:00
Lea Verou
2090b05e8a Move CSS preprocessing code to separate file 2025-02-20 13:17:44 -05:00
Lea Verou
3cdd8e49a1 Preprocess (some) stylesheets with Nunjucks 2025-02-20 13:17:02 -05:00
Lea Verou
d8673a7d71 Preprocess (some) stylesheets with Nunjucks
- Run Prettier on preprocessed CSS
- Add comment at the start of every generated CSS file
- Generate multiple files via interpolation in filenames!
- Preprocess brand/*.css, variants.css
2025-02-20 13:14:18 -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
76 changed files with 2775 additions and 547 deletions

View File

@@ -13,4 +13,4 @@ package-lock.json
tsconfig.json
cdn
_site
docs/assets/scripts/prism.js
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

@@ -5,12 +5,12 @@
<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 #}

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,11 +1,14 @@
{% set paletteId = palette.fileSlug or page.fileSlug %}
{% set tints = [80, 60, 40, 20] %}
{% 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

@@ -1,17 +1,74 @@
{% 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,
'tweaked-chroma': tweaked.chroma,
'tweaked-hue': tweaked.hue,
'tweaked-any': tweaked.chroma || tweaked.hue
}"
:style="{ '--chroma-scale': chromaScale }">
{% 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' %}
</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="brand">
<wa-icon name="sliders-simple" slot="icon" variant="regular"></wa-icon>
This palette has been tweaked.
<wa-tag v-for="tweakHumanReadable, param in tweaksHumanReadable" removable @wa-remove="removeTweak(param)">{% raw %}{{ tweakHumanReadable }}{% endraw %}</wa-tag>
<wa-button @click="reset" appearance="outlined">
<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">
<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>
@@ -21,17 +78,57 @@
{%- endfor %}
</tr>
</thead>
{# Initialize to last hue before gray #}
{%- set hueBefore = hues[hues|length - 2] -%}
{% for hue in hues -%}
<tr>
<th>{{ hue | capitalize }}</th>
<td class="core-column">
<div class="color swatch" style="background-color: var(--wa-color-{{ hue }}); color: var(--wa-color-{{ hue }}-{{ '05' if palettes[paletteId][hue].maxChromaTint > 60 else '95' }});">
{%- set coreColor = palettes[paletteId][hue][palettes[paletteId][hue].maxChromaTint] -%}
{%- set maxChroma = coreColor.c if coreColor.c > maxChroma else maxChroma -%}
<tr data-hue="{{ hue }}" class="color-scale" :class="{tweaking: tweaking.{{ hue }}, tweaked: hueShifts.{{ hue }} }"
:style="{ '--hue-shift': hueShifts.{{ hue }} || '' }">
<th>
{{ hue | capitalize }}
</th>
<td class="core-column" style="--color: var(--wa-color-{{ hue }})">
{% if hue !== 'gray' %}
{%- 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 -%}
<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-copy-button value="--wa-color-{{ hue }}" copy-label="--wa-color-{{ hue }}"></wa-copy-button>
<wa-icon name="sliders-simple" class="tweak-icon"></wa-icon>
</div>
<div class="popup">
<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>
<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>
{%- set hueBefore = hue -%}
{% else %}
<div id="core-{{ hue }}-swatch" class="color swatch" data-tint="core" 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 }}
</div>
{% endif %}
</td>
{% for tint in tints -%}
<td>
{%- set color = palettes[paletteId][hue][tint] -%}
<td data-tint="{{ tint }}" style="--color: var(--wa-color-{{ hue }}-{{ tint }})">
<div class="color swatch" style="background-color: var(--wa-color-{{ hue }}-{{ tint }})">
<wa-copy-button value="--wa-color-{{ hue }}-{{ tint }}" copy-label="--wa-color-{{ hue }}-{{ tint }}"></wa-copy-button>
</div>
@@ -41,6 +138,25 @@
{%- 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" 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">
@@ -65,6 +181,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 %}
@@ -84,6 +201,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 %}
@@ -102,6 +220,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 %}
@@ -114,13 +233,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

@@ -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

File diff suppressed because one or more lines are too long

View File

@@ -1,37 +0,0 @@
/**
* Get import code for remixed themes.
*/
export const urls = {
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`,
};
function getImport(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 getCode(base, params, options) {
let ret = [];
if (base) {
ret.push(`styles/themes/${base}.css`);
}
ret.push(
...Object.entries(params)
.filter(([aspect, id]) => Boolean(id))
.map(([aspect, id]) => urls[aspect](id)),
);
return ret.map(url => getImport(url, options)).join('\n');
}

View File

@@ -0,0 +1,206 @@
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();
},
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.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.saved = null;
}
},
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();
for (let prefix of prefixes) {
let a = document.querySelector(`#sidebar a[href^="${prefix}"]`);
if (a) {
for (let current of document.querySelectorAll('#sidebar a.current')) {
current.classList.remove('current');
}
a.classList.add('current');
break;
}
}
};
sidebar.render = function () {
this.palettes.render();
};
sidebar.render();
window.addEventListener('turbo:render', () => sidebar.render());

View File

@@ -0,0 +1,6 @@
/**
* Get import code for remixed themes and tweaked palettes.
*/
export { theme as 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,49 @@
/**
* 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>`;
}
}
export function theme(base, params, options) {
let ret = [];
if (base) {
ret.push(urls.theme(base));
}
ret.push(
...Object.entries(params)
.filter(([aspect, id]) => Boolean(id))
.map(([aspect, id]) => urls[aspect](id)),
);
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,34 @@
/**
* 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 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

@@ -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 */
@@ -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;
@@ -463,6 +511,55 @@ table.colors {
}
}
.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 */
.layout-example-boundary {
border: var(--wa-border-width-s) dashed var(--wa-color-neutral-border-normal);

View File

@@ -1,47 +0,0 @@
---
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

@@ -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,6 +1,7 @@
{
"layout": "palette.njk",
"tags": ["palettes", "palette"],
"wide": true,
"eleventyComputed": {
"snippet": ".wa-palette-{{ page.fileSlug }}",
"icon": "palette",

View File

@@ -0,0 +1,164 @@
: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));
&::part(base) {
background: linear-gradient(to right in oklch, 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)));
}
.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);
--color-tweaked: oklch(from var(--color) l calc(c * var(--chroma-scale)) h);
}
.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: oklch(from var(--color) l var(--tweak-c) var(--tweak-h));
--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: lch(from var(--color) l var(--tweak-c) var(--tweak-h));
--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);
}
}
}
.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;
}
}
[v-if='saved'] {
display: none;
}

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

@@ -0,0 +1,411 @@
// 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 { selectors, urls } from '../../assets/scripts/tweak/data.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);
}
}
}
}
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,
tweaking: {},
saved: null,
};
},
created() {
// 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)),
});
if (location.search) {
// Update from URL
this.permalink.writeTo(this.hueShifts);
if (this.permalink.has('chroma-scale')) {
this.chromaScale = Number(this.permalink.get('chroma-scale') || 1);
}
if (this.permalink.has('uid')) {
this.uid = Number(this.permalink.get('uid'));
}
let palette = { id: this.paletteId, uid: this.uid, search: location.search };
this.saved = sidebar.palette.getSaved(palette);
}
},
computed: {
global() {
return globalThis;
},
tweaks() {
return { hueShifts: this.hueShifts, chromaScale: this.chromaScale };
},
isTweaked() {
return Object.values(this.hueShifts).some(Boolean);
},
code() {
let ret = {};
for (let language of ['html', 'css']) {
let code = getPaletteCode(this.paletteId, this.tweaks, { language, cdnUrl });
ret[language] = {
raw: code,
highlighted: Prism.highlight(code, Prism.languages[language], language),
};
}
return ret;
},
colors() {
let ret = {};
for (let hue in this.originalColors) {
let originalScale = this.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 oklch = originalScale[tint].coords.slice();
if (this.hueShifts[hue]) {
oklch[2] += this.hueShifts[hue];
}
if (this.chromaScale !== 1) {
oklch[1] *= this.chromaScale;
}
scale[tint] = new Color('oklch', oklch);
}
}
return ret;
},
tweaked() {
return {
chroma: this.chromaScale !== 1,
hue: Object.values(this.hueShifts).some(Boolean),
};
},
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 =
{
red: 'redder',
orange: 'oranger',
indigo: 'more indigo',
}[relHue] ?? relHue + 'er';
ret[hue] = hueTweak + ' ' + hue + 's';
}
return ret;
},
originalContrasts() {
let ret = {};
for (let hue in this.originalColors) {
ret[hue] = {};
for (let tintBg of tints) {
ret[hue][tintBg] = {};
let bgColor = this.originalColors[hue][tintBg];
if (!bgColor || !bgColor.contrast) {
continue;
}
for (let tintFg of tints) {
let contrast = bgColor.contrast(this.originalColors[hue][tintFg], 'WCAG21');
ret[hue][tintBg][tintFg] = contrast;
}
}
}
return ret;
},
contrasts() {
let ret = {};
for (let hue in this.colors) {
ret[hue] = {};
for (let tintBg in this.colors[hue]) {
ret[hue][tintBg] = {};
let bgColor = this.colors[hue][tintBg];
for (let tintFg in this.colors[hue]) {
let fgColor = this.colors[hue][tintFg];
let value = bgColor.contrast(fgColor, 'WCAG21');
let original = this.originalContrasts[hue][tintBg][tintFg];
ret[hue][tintBg][tintFg] = { value, original, bgColor, fgColor };
}
}
}
return ret;
},
},
watch: {
hueShifts: {
deep: true,
handler() {
this.permalink.readFrom(this.hueShifts);
},
},
chromaScale() {
this.permalink.set('chroma-scale', this.chromaScale, 1);
},
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: {
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) {
this.uid = uid = sidebar.palette.getUid();
this.permalink.set('uid', uid);
this.permalink.updateLocation();
}
let palette = { title, id: this.paletteId, uid, search: location.search };
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);
this.saved = null;
},
reset() {
for (let hue in this.hueShifts) {
this.hueShifts[hue] = 0;
}
this.chromaScale = 1;
},
removeTweak(param) {
if (param === 'chromaScale') {
this.chromaScale = 1;
} else {
this.hueShifts[param] = 0;
}
},
},
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() {
globalThis.paletteApp?.unmount?.();
globalThis.paletteApp = createApp(paletteAppSpec).mount('#palette-app');
}
init();
addEventListener('turbo:render', init);
export function getPaletteCode(paletteId, tweaks, options) {
let palette = allPalettes[paletteId].colors;
let imports = [];
if (paletteId) {
imports.push(urls.palette(paletteId));
}
let css = '';
if (tweaks) {
let { hueShifts, chromaScale = 1 } = tweaks;
let declarations = [];
if (hueShifts || chromaScale !== 1) {
for (let hue in hueShifts) {
let shift = hueShifts[hue];
if ((!shift && chromaScale === 1) || hue === 'orange') {
continue;
}
let scale = palette[hue];
for (let tint of ['05', '10', '20', '30', '40', '50', '60', '70', '80', '90', '95']) {
let color = scale[tint];
if (Array.isArray(color)) {
color = new Color('oklch', coords);
} else {
color = color.clone();
}
color.set({ h: h => h + shift, c: c => c * chromaScale });
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];
}

View File

@@ -27,8 +27,9 @@ You can find them in the first column of each color palette.
### Themes
- 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.
- You can now override the brand color of any theme with any of the 9 hues supported.
- Improved UI for theme remixing, with previews and generated copyable code snippets
- Improved UI for theme remixing, with previews and generated copyable code snippets.
### Components
@@ -43,6 +44,7 @@ You can find them in the first column of each color palette.
- 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
- Fixed a bug in `<wa-select>` that prevented the description from being read by screen readers
### Docs

View File

@@ -34,7 +34,8 @@ eleventyComputed:
</wa-image-comparer>
<script type="module">
import { getCode, urls as stylesheetURLs } from "/assets/scripts/remix.js";
import { urls as stylesheetURLs } from "/assets/scripts/tweak/data.js";
import { theme as getThemeCode } from "/assets/scripts/tweak/code.js";
function updateTheme() {
let params = new URLSearchParams(window.location.search);
@@ -57,7 +58,7 @@ function updateTheme() {
}
let msgs = [];
let code = getCode("{{ theme.fileSlug }}", params, {attributes: 'class="mix-and-match"'});
let code = getThemeCode("{{ theme.fileSlug }}", params, {attributes: 'class="mix-and-match"'});
document.head.insertAdjacentHTML('beforeend', code);
for (let name in stylesheetURLs) {

View File

@@ -1,12 +1,6 @@
import { getCode } from '/assets/scripts/remix.js';
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)));
globalThis.Prism = globalThis.Prism || {};
globalThis.Prism.manual = true;
await import('/assets/scripts/prism.js');
Prism.plugins.customClass.prefix('code-');
const cdnUrl = document.documentElement.dataset.cdnUrl;
const domChange = document.startViewTransition ? document.startViewTransition.bind(document) : fn => fn();
@@ -57,17 +51,11 @@ function init() {
typography: '',
},
params: { colors: '', palette: '', brand: '', typography: '' },
urlParams: new URLSearchParams(location.search),
urlParams: new Permalink(),
};
// Read URL params and apply them. This facilitates permalinks.
if (location.search) {
for (let aspect in data.params) {
if (data.urlParams.has(aspect)) {
data.params[aspect] = data.urlParams.get(aspect);
}
}
}
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
@@ -133,16 +121,11 @@ function render(changedAspect) {
for (let aspect in data.params) {
let value = data.params[aspect];
if (value) {
data.urlParams.set(aspect, value);
} else {
data.urlParams.delete(aspect);
}
selects[aspect].value = value;
}
data.urlParams.readFrom(data.params);
// Update demo URL
domChange(() => {
url.search = data.urlParams;
@@ -150,18 +133,14 @@ function render(changedAspect) {
return new Promise(resolve => (demo.onload = resolve));
});
// Update page URL. If theres already a search, replace it.
// We dont want to clog the users history while they iterate
let historyAction = location.search ? 'replaceState' : 'pushState';
history[historyAction](null, '', `?${data.urlParams}`);
// Update page URL
data.urlParams.updateLocation();
// Update code snippets
for (let language in codeSnippets) {
let codeSnippet = codeSnippets[language];
let copyButton = codeSnippet.previousElementSibling;
let code = getCode(data.baseTheme, data.params, { language, cdnUrl });
let code = getThemeCode(data.baseTheme, data.params, { language, cdnUrl });
codeSnippet.textContent = code;
copyButton.value = code;
Prism.highlightElement(codeSnippet);
}
}

View File

@@ -1,6 +1,7 @@
---
title: Color
description: Ensure consistent use of color and readable contrast with Web Awesome's color properties.
wide: true
---
<style>
@@ -43,6 +44,18 @@ description: Ensure consistent use of color and readable contrast with Web Aweso
color: var(--wa-color-brand-on-loud);
text-align: center;
}
.colors.bounded,
.core-column {
.color.swatch {
--key: var(--core-key);
--lt-60: calc(clamp(0, 60 - var(--key), 1) * 100%);
&::before {
counter-reset: key var(--key);
content: counter(key);
}
}
}
</style>
Web Awesome's color system is made up of CSS custom properties to help with consistent color use throughout your project.
@@ -64,21 +77,125 @@ You can use these numbers to ensure accessible color contrast per [WCAG 2.1 succ
- A difference of 50 ensures a minimum 4.5:1 contrast ratio, suitable for normal text (AA) and large text (AAA)
- A difference of 60 ensures a minimum 7:1 contrast ratio, suitable for all text (AAA)
Each Web Awesome palette defines seven literal colors each with 11 lightness values using the format `--wa-color-{hue}-{tint}`.
Each Web Awesome palette defines eight literal colors each with 11 lightness values using the format `--wa-color-{hue}-{tint}`.
Additionally, the "core" (most vibrant) color of each scale is mapped to `--wa-color-{hue}`.
{% set tints = ["95", "90", "80", "70", "60", "50", "40", "30", "20", "10", "05"] -%}
<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 -%}
<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"] -%}
<li class="color-preview">
<tr style="--core-key: var(--wa-color-{{ hue }}-key)">
<th>{{ hue | capitalize }}</th>
<td class="core-column" style="--color: var(--wa-color-{{ hue }})">
<div class="color swatch" style="background-color: var(--wa-color-{{ hue }}); color: var(--wa-color-{{ hue }}-on);">
<wa-copy-button value="--wa-color-{{ hue }}" copy-label="--wa-color-{{ hue }}"></wa-copy-button>
</div>
</td>
{% for tint in tints -%}
<td data-tint="{{ tint }}" style="--color: var(--wa-color-{{ hue }}-{{ tint }})">
<div class="color swatch" style="background-color: var(--wa-color-{{ hue }}-{{ tint }})">
<wa-copy-button value="--wa-color-{{ hue }}-{{ tint }}" copy-label="--wa-color-{{ hue }}-{{ tint }}"></wa-copy-button>
</div>
<small>{{ tint }}</small>
</li>
{%- endfor %}
</ul>
</td>
{%- endfor -%}
</tr>
{%- endfor %}
</table>
## Bounded Core Colors
In addition to the literal color scales above and their core colors,
we also define variables for "bounded" core colors, which resolve to the core color of the hue's scale,
but clamped to a specific tint.
This is useful for using core colors while ensuring a minimum contrast ratio with other colors in your theme.
### Upper Bound Core Colors
These variables resolve to the core color as long as it is equal or below the specified tint,
and to the specified tint otherwise.
{% set tints_clamped = ['40', '50', '60', '70'] %}
<table class="colors bounded">
<thead>
<tr>
<th></th>
<th class="core-column">Core tint</th>
{% for tint in tints_clamped -%}
<th>max-{{ tint }}</th>
{%- endfor %}
</tr>
</thead>
{% for hue in hues -%}
<tr style="--core-key: var(--wa-color-{{ hue }}-key)">
<th>{{ hue | capitalize }}</th>
<td class="core-column">
<div class="color swatch" style="background-color: var(--wa-color-{{ hue }}); color: var(--wa-color-{{ hue }}-on);">
{{ palettes[paletteId][hue].maxChromaTint -}}
<wa-copy-button value="--wa-color-{{ hue }}" copy-label="--wa-color-{{ hue }}"></wa-copy-button>
</div>
</td>
{% for tint in tints_clamped -%}
<td>
<div class="color swatch" style="
--key: min(var(--core-key), {{ tint }});
color: color-mix(in oklab, var(--wa-color-{{ hue }}-10), white var(--lt-60));
background-color: var(--wa-color-{{ hue }}-max-{{ tint }});">
<wa-copy-button value="--wa-color-{{ hue }}-max-{{ tint }}" copy-label="--wa-color-{{ hue }}-max-{{ tint }}"></wa-copy-button>
</div>
</td>
{%- endfor -%}
</tr>
{%- endfor %}
</table>
### Lower Bound Core Colors
These variables resolve to the core color as long as it is at least at the specified tint,
and to the specified tint otherwise.
<table class="colors bounded">
<thead>
<tr>
<th></th>
<th class="core-column">Core tint</th>
{% for tint in tints_clamped -%}
<th>min-{{ tint }}</th>
{%- endfor %}
</tr>
</thead>
{% for hue in hues -%}
<tr style="--core-key: var(--wa-color-{{ hue }}-key)">
<th>{{ hue | capitalize }}</th>
<td class="core-column">
<div class="color swatch" style="background-color: var(--wa-color-{{ hue }}); color: var(--wa-color-{{ hue }}-on);">
{{ palettes[paletteId][hue].maxChromaTint -}}
<wa-copy-button value="--wa-color-{{ hue }}" copy-label="--wa-color-{{ hue }}"></wa-copy-button>
</div>
</td>
{% for tint in tints_clamped -%}
<td>
<div class="color swatch" style="
--key: max(var(--core-key), {{ tint }});
color: color-mix(in oklab, var(--wa-color-{{ hue }}-10), white var(--lt-60));
background-color: var(--wa-color-{{ hue }}-min-{{ tint }})">
<wa-copy-button value="--wa-color-{{ hue }}-min-{{ tint }}" copy-label="--wa-color-{{ hue }}-min-{{ tint }}"></wa-copy-button>
</div>
</td>
{%- endfor -%}
</tr>
{%- endfor %}
</table>
## Foundational Colors

View File

@@ -12,6 +12,7 @@ import { dirname, join, relative } from 'path';
import process from 'process';
import copy from 'recursive-copy';
import { fileURLToPath } from 'url';
import { preprocessStylesheet } from './preprocess-css.js';
import { cdnDir, distDir, docsDir, rootDir, runScript, siteDir } from './utils.js';
const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -36,6 +37,7 @@ async function buildAll() {
await generateManifest();
await generateReactWrappers();
await generateTypes();
await preprocessStyles();
await generateStyles();
// copy everything to unbundled before we generate bundles.
@@ -105,6 +107,23 @@ function generateReactWrappers() {
return Promise.resolve();
}
/**
* Generate preprocessed CSS
*/
async function preprocessStyles() {
const preprocessedCSSFiles = await globby(join(rootDir, 'src/styles/**/*.css.njk'));
if (preprocessedCSSFiles.length > 0) {
spinner.start('Preprocessing stylesheets');
for (let filePath of preprocessedCSSFiles) {
await preprocessStylesheet(filePath);
}
spinner.succeed();
}
}
/**
* Copies theme stylesheets to the dist.
*/
@@ -323,6 +342,7 @@ if (isDeveloping) {
bs.init(
{
startPath: '/',
open: false,
port,
logLevel: 'silent',
logPrefix: '[webawesome]',
@@ -371,6 +391,7 @@ if (isDeveloping) {
try {
const isTestFile = filename.includes('.test.ts');
const isCssStylesheet = filename.includes('.css');
const isPreprocessedStylesheet = filename.endsWith('.css.njk');
const isComponent =
filename.includes('components/') && filename.includes('.ts') && !isCssStylesheet && !isTestFile;
@@ -381,6 +402,10 @@ if (isDeveloping) {
await regenerateBundle();
if (isPreprocessedStylesheet || filename.endsWith('src/styles/data.js')) {
await preprocessStyles();
}
// Copy stylesheets when CSS files change
if (isCssStylesheet) {
await generateStyles();

61
scripts/preprocess-css.js Normal file
View File

@@ -0,0 +1,61 @@
import { readFile, writeFile } from 'fs/promises';
import nunjucks from 'nunjucks';
import * as prettier from 'prettier';
import prettierConfig from '../prettier.config.js';
import * as globalData from '../src/styles/data.js';
const prelude = inputFilename => `/* DO NOT EDIT THIS FILE. It is generated from "${inputFilename}" */`;
let env = nunjucks.configure({ autoescape: false });
let filenameVariables = Object.keys(globalData)
.filter(key => key.endsWith('s'))
.map(key => key.slice(0, -1));
let filenameVariablesRegex = RegExp(`\\{\\{\\s*(${filenameVariables.join('|')})\\s*\\}\\}`, 'g');
export async function preprocessStylesheet(inputPath) {
const content = await readFile(inputPath, 'utf-8');
let outputPath = inputPath.replace(/\.njk$/, '');
let filename = outputPath.split('/').pop();
if (filenameVariablesRegex.test(filename)) {
// NOTE only supports a single variable right now
filenameVariablesRegex.lastIndex = 0;
let ret = [];
for (let match of filename.matchAll(filenameVariablesRegex)) {
let variable = match[1];
let values = globalData[variable + 's'];
for (let value of values) {
let data = { [variable]: value };
let css = await renderCSS(content, { data, inputPath, outputPath });
// Now use Nunjucks *again*, to render the actual filename
let localOutputFilePath = env.renderString(outputPath, data);
ret.push(writeFile(localOutputFilePath, css, 'utf-8'));
}
}
return Promise.all(ret);
} else {
let css = await renderCSS(content, { inputPath, outputPath });
return writeFile(outputPath, css, 'utf-8');
}
// TODO add generated files to .gitignore?
}
export async function renderCSS(content, { data, inputPath, outputPath } = {}) {
let inputFilename = inputPath.split('/').pop();
data = data ? { ...globalData, ...data } : globalData;
let css = env.renderString(content, data);
if (prelude) {
css = prelude(inputFilename) + '\n' + css;
}
css = await prettier.format(css, { ...prettierConfig, filepath: outputPath });
return css;
}

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

@@ -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;

View File

@@ -927,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

@@ -1,63 +0,0 @@
: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)
);
/* Text color: white if key < 70, brand-10 otherwise */
--wa-color-brand-on: color-mix(in oklab, var(--wa-color-brand-10) var(--wa-color-brand-if-gte-60), white);
}

View File

@@ -1,5 +1,4 @@
@import url('base.css');
/* DO NOT EDIT THIS FILE. It is generated from "{{ hue }}.css.njk" */
:where(:root),
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']),
@@ -18,4 +17,30 @@
--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);
--wa-color-brand-lt-40: var(--wa-color-blue-lt-40);
--wa-color-brand-gte-40: var(--wa-color-blue-gte-40);
--wa-color-brand-lt-50: var(--wa-color-blue-lt-50);
--wa-color-brand-gte-50: var(--wa-color-blue-gte-50);
--wa-color-brand-lt-60: var(--wa-color-blue-lt-60);
--wa-color-brand-gte-60: var(--wa-color-blue-gte-60);
--wa-color-brand-lt-70: var(--wa-color-blue-lt-70);
--wa-color-brand-gte-70: var(--wa-color-blue-gte-70);
--wa-color-brand-lt-80: var(--wa-color-blue-lt-80);
--wa-color-brand-gte-80: var(--wa-color-blue-gte-80);
--wa-color-brand-max-40: var(--wa-color-blue-max-40);
--wa-color-brand-min-40: var(--wa-color-blue-min-40);
--wa-color-brand-max-50: var(--wa-color-blue-max-50);
--wa-color-brand-min-50: var(--wa-color-blue-min-50);
--wa-color-brand-max-60: var(--wa-color-blue-max-60);
--wa-color-brand-min-60: var(--wa-color-blue-min-60);
--wa-color-brand-max-70: var(--wa-color-blue-max-70);
--wa-color-brand-min-70: var(--wa-color-blue-min-70);
--wa-color-brand-on: var(--wa-color-blue-on);
}

View File

@@ -1,5 +1,4 @@
@import url('base.css');
/* DO NOT EDIT THIS FILE. It is generated from "{{ hue }}.css.njk" */
:where(:root),
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']),
@@ -18,4 +17,30 @@
--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);
--wa-color-brand-lt-40: var(--wa-color-cyan-lt-40);
--wa-color-brand-gte-40: var(--wa-color-cyan-gte-40);
--wa-color-brand-lt-50: var(--wa-color-cyan-lt-50);
--wa-color-brand-gte-50: var(--wa-color-cyan-gte-50);
--wa-color-brand-lt-60: var(--wa-color-cyan-lt-60);
--wa-color-brand-gte-60: var(--wa-color-cyan-gte-60);
--wa-color-brand-lt-70: var(--wa-color-cyan-lt-70);
--wa-color-brand-gte-70: var(--wa-color-cyan-gte-70);
--wa-color-brand-lt-80: var(--wa-color-cyan-lt-80);
--wa-color-brand-gte-80: var(--wa-color-cyan-gte-80);
--wa-color-brand-max-40: var(--wa-color-cyan-max-40);
--wa-color-brand-min-40: var(--wa-color-cyan-min-40);
--wa-color-brand-max-50: var(--wa-color-cyan-max-50);
--wa-color-brand-min-50: var(--wa-color-cyan-min-50);
--wa-color-brand-max-60: var(--wa-color-cyan-max-60);
--wa-color-brand-min-60: var(--wa-color-cyan-min-60);
--wa-color-brand-max-70: var(--wa-color-cyan-max-70);
--wa-color-brand-min-70: var(--wa-color-cyan-min-70);
--wa-color-brand-on: var(--wa-color-cyan-on);
}

View File

@@ -1,5 +1,4 @@
@import url('base.css');
/* DO NOT EDIT THIS FILE. It is generated from "{{ hue }}.css.njk" */
:where(:root),
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']),
@@ -18,4 +17,30 @@
--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);
--wa-color-brand-lt-40: var(--wa-color-gray-lt-40);
--wa-color-brand-gte-40: var(--wa-color-gray-gte-40);
--wa-color-brand-lt-50: var(--wa-color-gray-lt-50);
--wa-color-brand-gte-50: var(--wa-color-gray-gte-50);
--wa-color-brand-lt-60: var(--wa-color-gray-lt-60);
--wa-color-brand-gte-60: var(--wa-color-gray-gte-60);
--wa-color-brand-lt-70: var(--wa-color-gray-lt-70);
--wa-color-brand-gte-70: var(--wa-color-gray-gte-70);
--wa-color-brand-lt-80: var(--wa-color-gray-lt-80);
--wa-color-brand-gte-80: var(--wa-color-gray-gte-80);
--wa-color-brand-max-40: var(--wa-color-gray-max-40);
--wa-color-brand-min-40: var(--wa-color-gray-min-40);
--wa-color-brand-max-50: var(--wa-color-gray-max-50);
--wa-color-brand-min-50: var(--wa-color-gray-min-50);
--wa-color-brand-max-60: var(--wa-color-gray-max-60);
--wa-color-brand-min-60: var(--wa-color-gray-min-60);
--wa-color-brand-max-70: var(--wa-color-gray-max-70);
--wa-color-brand-min-70: var(--wa-color-gray-min-70);
--wa-color-brand-on: var(--wa-color-gray-on);
}

View File

@@ -1,5 +1,4 @@
@import url('base.css');
/* DO NOT EDIT THIS FILE. It is generated from "{{ hue }}.css.njk" */
:where(:root),
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']),
@@ -18,4 +17,30 @@
--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);
--wa-color-brand-lt-40: var(--wa-color-green-lt-40);
--wa-color-brand-gte-40: var(--wa-color-green-gte-40);
--wa-color-brand-lt-50: var(--wa-color-green-lt-50);
--wa-color-brand-gte-50: var(--wa-color-green-gte-50);
--wa-color-brand-lt-60: var(--wa-color-green-lt-60);
--wa-color-brand-gte-60: var(--wa-color-green-gte-60);
--wa-color-brand-lt-70: var(--wa-color-green-lt-70);
--wa-color-brand-gte-70: var(--wa-color-green-gte-70);
--wa-color-brand-lt-80: var(--wa-color-green-lt-80);
--wa-color-brand-gte-80: var(--wa-color-green-gte-80);
--wa-color-brand-max-40: var(--wa-color-green-max-40);
--wa-color-brand-min-40: var(--wa-color-green-min-40);
--wa-color-brand-max-50: var(--wa-color-green-max-50);
--wa-color-brand-min-50: var(--wa-color-green-min-50);
--wa-color-brand-max-60: var(--wa-color-green-max-60);
--wa-color-brand-min-60: var(--wa-color-green-min-60);
--wa-color-brand-max-70: var(--wa-color-green-max-70);
--wa-color-brand-min-70: var(--wa-color-green-min-70);
--wa-color-brand-on: var(--wa-color-green-on);
}

View File

@@ -1,5 +1,4 @@
@import url('base.css');
/* DO NOT EDIT THIS FILE. It is generated from "{{ hue }}.css.njk" */
:where(:root),
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']),
@@ -18,4 +17,30 @@
--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);
--wa-color-brand-lt-40: var(--wa-color-indigo-lt-40);
--wa-color-brand-gte-40: var(--wa-color-indigo-gte-40);
--wa-color-brand-lt-50: var(--wa-color-indigo-lt-50);
--wa-color-brand-gte-50: var(--wa-color-indigo-gte-50);
--wa-color-brand-lt-60: var(--wa-color-indigo-lt-60);
--wa-color-brand-gte-60: var(--wa-color-indigo-gte-60);
--wa-color-brand-lt-70: var(--wa-color-indigo-lt-70);
--wa-color-brand-gte-70: var(--wa-color-indigo-gte-70);
--wa-color-brand-lt-80: var(--wa-color-indigo-lt-80);
--wa-color-brand-gte-80: var(--wa-color-indigo-gte-80);
--wa-color-brand-max-40: var(--wa-color-indigo-max-40);
--wa-color-brand-min-40: var(--wa-color-indigo-min-40);
--wa-color-brand-max-50: var(--wa-color-indigo-max-50);
--wa-color-brand-min-50: var(--wa-color-indigo-min-50);
--wa-color-brand-max-60: var(--wa-color-indigo-max-60);
--wa-color-brand-min-60: var(--wa-color-indigo-min-60);
--wa-color-brand-max-70: var(--wa-color-indigo-max-70);
--wa-color-brand-min-70: var(--wa-color-indigo-min-70);
--wa-color-brand-on: var(--wa-color-indigo-on);
}

View File

@@ -1,21 +0,0 @@
@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);
}

View File

@@ -1,5 +1,4 @@
@import url('base.css');
/* DO NOT EDIT THIS FILE. It is generated from "{{ hue }}.css.njk" */
:where(:root),
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']),
@@ -18,4 +17,30 @@
--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);
--wa-color-brand-lt-40: var(--wa-color-pink-lt-40);
--wa-color-brand-gte-40: var(--wa-color-pink-gte-40);
--wa-color-brand-lt-50: var(--wa-color-pink-lt-50);
--wa-color-brand-gte-50: var(--wa-color-pink-gte-50);
--wa-color-brand-lt-60: var(--wa-color-pink-lt-60);
--wa-color-brand-gte-60: var(--wa-color-pink-gte-60);
--wa-color-brand-lt-70: var(--wa-color-pink-lt-70);
--wa-color-brand-gte-70: var(--wa-color-pink-gte-70);
--wa-color-brand-lt-80: var(--wa-color-pink-lt-80);
--wa-color-brand-gte-80: var(--wa-color-pink-gte-80);
--wa-color-brand-max-40: var(--wa-color-pink-max-40);
--wa-color-brand-min-40: var(--wa-color-pink-min-40);
--wa-color-brand-max-50: var(--wa-color-pink-max-50);
--wa-color-brand-min-50: var(--wa-color-pink-min-50);
--wa-color-brand-max-60: var(--wa-color-pink-max-60);
--wa-color-brand-min-60: var(--wa-color-pink-min-60);
--wa-color-brand-max-70: var(--wa-color-pink-max-70);
--wa-color-brand-min-70: var(--wa-color-pink-min-70);
--wa-color-brand-on: var(--wa-color-pink-on);
}

View File

@@ -1,5 +1,4 @@
@import url('base.css');
/* DO NOT EDIT THIS FILE. It is generated from "{{ hue }}.css.njk" */
:where(:root),
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']),
@@ -18,4 +17,30 @@
--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);
--wa-color-brand-lt-40: var(--wa-color-purple-lt-40);
--wa-color-brand-gte-40: var(--wa-color-purple-gte-40);
--wa-color-brand-lt-50: var(--wa-color-purple-lt-50);
--wa-color-brand-gte-50: var(--wa-color-purple-gte-50);
--wa-color-brand-lt-60: var(--wa-color-purple-lt-60);
--wa-color-brand-gte-60: var(--wa-color-purple-gte-60);
--wa-color-brand-lt-70: var(--wa-color-purple-lt-70);
--wa-color-brand-gte-70: var(--wa-color-purple-gte-70);
--wa-color-brand-lt-80: var(--wa-color-purple-lt-80);
--wa-color-brand-gte-80: var(--wa-color-purple-gte-80);
--wa-color-brand-max-40: var(--wa-color-purple-max-40);
--wa-color-brand-min-40: var(--wa-color-purple-min-40);
--wa-color-brand-max-50: var(--wa-color-purple-max-50);
--wa-color-brand-min-50: var(--wa-color-purple-min-50);
--wa-color-brand-max-60: var(--wa-color-purple-max-60);
--wa-color-brand-min-60: var(--wa-color-purple-min-60);
--wa-color-brand-max-70: var(--wa-color-purple-max-70);
--wa-color-brand-min-70: var(--wa-color-purple-min-70);
--wa-color-brand-on: var(--wa-color-purple-on);
}

View File

@@ -1,5 +1,4 @@
@import url('base.css');
/* DO NOT EDIT THIS FILE. It is generated from "{{ hue }}.css.njk" */
:where(:root),
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']),
@@ -18,4 +17,30 @@
--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);
--wa-color-brand-lt-40: var(--wa-color-red-lt-40);
--wa-color-brand-gte-40: var(--wa-color-red-gte-40);
--wa-color-brand-lt-50: var(--wa-color-red-lt-50);
--wa-color-brand-gte-50: var(--wa-color-red-gte-50);
--wa-color-brand-lt-60: var(--wa-color-red-lt-60);
--wa-color-brand-gte-60: var(--wa-color-red-gte-60);
--wa-color-brand-lt-70: var(--wa-color-red-lt-70);
--wa-color-brand-gte-70: var(--wa-color-red-gte-70);
--wa-color-brand-lt-80: var(--wa-color-red-lt-80);
--wa-color-brand-gte-80: var(--wa-color-red-gte-80);
--wa-color-brand-max-40: var(--wa-color-red-max-40);
--wa-color-brand-min-40: var(--wa-color-red-min-40);
--wa-color-brand-max-50: var(--wa-color-red-max-50);
--wa-color-brand-min-50: var(--wa-color-red-min-50);
--wa-color-brand-max-60: var(--wa-color-red-max-60);
--wa-color-brand-min-60: var(--wa-color-red-min-60);
--wa-color-brand-max-70: var(--wa-color-red-max-70);
--wa-color-brand-min-70: var(--wa-color-red-min-70);
--wa-color-brand-on: var(--wa-color-red-on);
}

View File

@@ -1,5 +1,4 @@
@import url('base.css');
/* DO NOT EDIT THIS FILE. It is generated from "{{ hue }}.css.njk" */
:where(:root),
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']),
@@ -18,4 +17,30 @@
--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);
--wa-color-brand-lt-40: var(--wa-color-yellow-lt-40);
--wa-color-brand-gte-40: var(--wa-color-yellow-gte-40);
--wa-color-brand-lt-50: var(--wa-color-yellow-lt-50);
--wa-color-brand-gte-50: var(--wa-color-yellow-gte-50);
--wa-color-brand-lt-60: var(--wa-color-yellow-lt-60);
--wa-color-brand-gte-60: var(--wa-color-yellow-gte-60);
--wa-color-brand-lt-70: var(--wa-color-yellow-lt-70);
--wa-color-brand-gte-70: var(--wa-color-yellow-gte-70);
--wa-color-brand-lt-80: var(--wa-color-yellow-lt-80);
--wa-color-brand-gte-80: var(--wa-color-yellow-gte-80);
--wa-color-brand-max-40: var(--wa-color-yellow-max-40);
--wa-color-brand-min-40: var(--wa-color-yellow-min-40);
--wa-color-brand-max-50: var(--wa-color-yellow-max-50);
--wa-color-brand-min-50: var(--wa-color-yellow-min-50);
--wa-color-brand-max-60: var(--wa-color-yellow-max-60);
--wa-color-brand-min-60: var(--wa-color-yellow-min-60);
--wa-color-brand-max-70: var(--wa-color-yellow-max-70);
--wa-color-brand-min-70: var(--wa-color-yellow-min-70);
--wa-color-brand-on: var(--wa-color-yellow-on);
}

View File

@@ -0,0 +1,22 @@
:where(:root),
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']),
:where([class^='wa-palette-'], [class*=' wa-palette-']),
.wa-brand-{{ hue }} {
{%- for tint in tints | reverse %}
--wa-color-brand-{{ tint }}: var(--wa-color-{{ hue }}-{{ tint }});
{%- endfor %}
--wa-color-brand: var(--wa-color-{{ hue }});
--wa-color-brand-key: var(--wa-color-{{ hue }}-key);
{% for tint in ['40', '50', '60', '70', '80'] %}
--wa-color-brand-lt-{{ tint }}: var(--wa-color-{{ hue }}-lt-{{ tint }});
--wa-color-brand-gte-{{ tint }}: var(--wa-color-{{ hue }}-gte-{{ tint }});
{% endfor %}
{% for tint in ['40', '50', '60', '70'] %}
--wa-color-brand-max-{{ tint }}: var(--wa-color-{{ hue }}-max-{{ tint }});
--wa-color-brand-min-{{ tint }}: var(--wa-color-{{ hue }}-min-{{ tint }});
{%- endfor %}
--wa-color-brand-on: var(--wa-color-{{ hue }}-on);
}

View File

@@ -1,3 +1,5 @@
@import url('base.css');
:where(:root),
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']),

403
src/styles/color/base.css Normal file
View File

@@ -0,0 +1,403 @@
/* DO NOT EDIT THIS FILE. It is generated from "base.css.njk" */
:where([class^='wa-theme-'], [class*=' wa-theme-']),
:where([class^='wa-palette-'], [class*=' wa-palette-']),
:where([class^='wa-brand-'], [class*=' wa-brand-']),
:where(:root),
:host {
/**
* Conditional tokens for use in color-mix()
* --wa-color-{hue}-lt-N ➡️ 100% if key < N, 0% otherwise
* --wa-color-{hue}-gte-N ➡️ 100% if key >= N, 0% otherwise
*/
--wa-color-red-lt-40: calc(clamp(0, 40 - var(--wa-color-red-key), 1) * 100%);
--wa-color-red-gte-40: calc(100% - var(--wa-color-red-lt-40));
--wa-color-red-lt-50: calc(clamp(0, 50 - var(--wa-color-red-key), 1) * 100%);
--wa-color-red-gte-50: calc(100% - var(--wa-color-red-lt-50));
--wa-color-red-lt-60: calc(clamp(0, 60 - var(--wa-color-red-key), 1) * 100%);
--wa-color-red-gte-60: calc(100% - var(--wa-color-red-lt-60));
--wa-color-red-lt-70: calc(clamp(0, 70 - var(--wa-color-red-key), 1) * 100%);
--wa-color-red-gte-70: calc(100% - var(--wa-color-red-lt-70));
--wa-color-red-lt-80: calc(clamp(0, 80 - var(--wa-color-red-key), 1) * 100%);
--wa-color-red-gte-80: calc(100% - var(--wa-color-red-lt-80));
--wa-color-yellow-lt-40: calc(clamp(0, 40 - var(--wa-color-yellow-key), 1) * 100%);
--wa-color-yellow-gte-40: calc(100% - var(--wa-color-yellow-lt-40));
--wa-color-yellow-lt-50: calc(clamp(0, 50 - var(--wa-color-yellow-key), 1) * 100%);
--wa-color-yellow-gte-50: calc(100% - var(--wa-color-yellow-lt-50));
--wa-color-yellow-lt-60: calc(clamp(0, 60 - var(--wa-color-yellow-key), 1) * 100%);
--wa-color-yellow-gte-60: calc(100% - var(--wa-color-yellow-lt-60));
--wa-color-yellow-lt-70: calc(clamp(0, 70 - var(--wa-color-yellow-key), 1) * 100%);
--wa-color-yellow-gte-70: calc(100% - var(--wa-color-yellow-lt-70));
--wa-color-yellow-lt-80: calc(clamp(0, 80 - var(--wa-color-yellow-key), 1) * 100%);
--wa-color-yellow-gte-80: calc(100% - var(--wa-color-yellow-lt-80));
--wa-color-green-lt-40: calc(clamp(0, 40 - var(--wa-color-green-key), 1) * 100%);
--wa-color-green-gte-40: calc(100% - var(--wa-color-green-lt-40));
--wa-color-green-lt-50: calc(clamp(0, 50 - var(--wa-color-green-key), 1) * 100%);
--wa-color-green-gte-50: calc(100% - var(--wa-color-green-lt-50));
--wa-color-green-lt-60: calc(clamp(0, 60 - var(--wa-color-green-key), 1) * 100%);
--wa-color-green-gte-60: calc(100% - var(--wa-color-green-lt-60));
--wa-color-green-lt-70: calc(clamp(0, 70 - var(--wa-color-green-key), 1) * 100%);
--wa-color-green-gte-70: calc(100% - var(--wa-color-green-lt-70));
--wa-color-green-lt-80: calc(clamp(0, 80 - var(--wa-color-green-key), 1) * 100%);
--wa-color-green-gte-80: calc(100% - var(--wa-color-green-lt-80));
--wa-color-cyan-lt-40: calc(clamp(0, 40 - var(--wa-color-cyan-key), 1) * 100%);
--wa-color-cyan-gte-40: calc(100% - var(--wa-color-cyan-lt-40));
--wa-color-cyan-lt-50: calc(clamp(0, 50 - var(--wa-color-cyan-key), 1) * 100%);
--wa-color-cyan-gte-50: calc(100% - var(--wa-color-cyan-lt-50));
--wa-color-cyan-lt-60: calc(clamp(0, 60 - var(--wa-color-cyan-key), 1) * 100%);
--wa-color-cyan-gte-60: calc(100% - var(--wa-color-cyan-lt-60));
--wa-color-cyan-lt-70: calc(clamp(0, 70 - var(--wa-color-cyan-key), 1) * 100%);
--wa-color-cyan-gte-70: calc(100% - var(--wa-color-cyan-lt-70));
--wa-color-cyan-lt-80: calc(clamp(0, 80 - var(--wa-color-cyan-key), 1) * 100%);
--wa-color-cyan-gte-80: calc(100% - var(--wa-color-cyan-lt-80));
--wa-color-blue-lt-40: calc(clamp(0, 40 - var(--wa-color-blue-key), 1) * 100%);
--wa-color-blue-gte-40: calc(100% - var(--wa-color-blue-lt-40));
--wa-color-blue-lt-50: calc(clamp(0, 50 - var(--wa-color-blue-key), 1) * 100%);
--wa-color-blue-gte-50: calc(100% - var(--wa-color-blue-lt-50));
--wa-color-blue-lt-60: calc(clamp(0, 60 - var(--wa-color-blue-key), 1) * 100%);
--wa-color-blue-gte-60: calc(100% - var(--wa-color-blue-lt-60));
--wa-color-blue-lt-70: calc(clamp(0, 70 - var(--wa-color-blue-key), 1) * 100%);
--wa-color-blue-gte-70: calc(100% - var(--wa-color-blue-lt-70));
--wa-color-blue-lt-80: calc(clamp(0, 80 - var(--wa-color-blue-key), 1) * 100%);
--wa-color-blue-gte-80: calc(100% - var(--wa-color-blue-lt-80));
--wa-color-indigo-lt-40: calc(clamp(0, 40 - var(--wa-color-indigo-key), 1) * 100%);
--wa-color-indigo-gte-40: calc(100% - var(--wa-color-indigo-lt-40));
--wa-color-indigo-lt-50: calc(clamp(0, 50 - var(--wa-color-indigo-key), 1) * 100%);
--wa-color-indigo-gte-50: calc(100% - var(--wa-color-indigo-lt-50));
--wa-color-indigo-lt-60: calc(clamp(0, 60 - var(--wa-color-indigo-key), 1) * 100%);
--wa-color-indigo-gte-60: calc(100% - var(--wa-color-indigo-lt-60));
--wa-color-indigo-lt-70: calc(clamp(0, 70 - var(--wa-color-indigo-key), 1) * 100%);
--wa-color-indigo-gte-70: calc(100% - var(--wa-color-indigo-lt-70));
--wa-color-indigo-lt-80: calc(clamp(0, 80 - var(--wa-color-indigo-key), 1) * 100%);
--wa-color-indigo-gte-80: calc(100% - var(--wa-color-indigo-lt-80));
--wa-color-purple-lt-40: calc(clamp(0, 40 - var(--wa-color-purple-key), 1) * 100%);
--wa-color-purple-gte-40: calc(100% - var(--wa-color-purple-lt-40));
--wa-color-purple-lt-50: calc(clamp(0, 50 - var(--wa-color-purple-key), 1) * 100%);
--wa-color-purple-gte-50: calc(100% - var(--wa-color-purple-lt-50));
--wa-color-purple-lt-60: calc(clamp(0, 60 - var(--wa-color-purple-key), 1) * 100%);
--wa-color-purple-gte-60: calc(100% - var(--wa-color-purple-lt-60));
--wa-color-purple-lt-70: calc(clamp(0, 70 - var(--wa-color-purple-key), 1) * 100%);
--wa-color-purple-gte-70: calc(100% - var(--wa-color-purple-lt-70));
--wa-color-purple-lt-80: calc(clamp(0, 80 - var(--wa-color-purple-key), 1) * 100%);
--wa-color-purple-gte-80: calc(100% - var(--wa-color-purple-lt-80));
--wa-color-pink-lt-40: calc(clamp(0, 40 - var(--wa-color-pink-key), 1) * 100%);
--wa-color-pink-gte-40: calc(100% - var(--wa-color-pink-lt-40));
--wa-color-pink-lt-50: calc(clamp(0, 50 - var(--wa-color-pink-key), 1) * 100%);
--wa-color-pink-gte-50: calc(100% - var(--wa-color-pink-lt-50));
--wa-color-pink-lt-60: calc(clamp(0, 60 - var(--wa-color-pink-key), 1) * 100%);
--wa-color-pink-gte-60: calc(100% - var(--wa-color-pink-lt-60));
--wa-color-pink-lt-70: calc(clamp(0, 70 - var(--wa-color-pink-key), 1) * 100%);
--wa-color-pink-gte-70: calc(100% - var(--wa-color-pink-lt-70));
--wa-color-pink-lt-80: calc(clamp(0, 80 - var(--wa-color-pink-key), 1) * 100%);
--wa-color-pink-gte-80: calc(100% - var(--wa-color-pink-lt-80));
--wa-color-gray-lt-40: calc(clamp(0, 40 - var(--wa-color-gray-key), 1) * 100%);
--wa-color-gray-gte-40: calc(100% - var(--wa-color-gray-lt-40));
--wa-color-gray-lt-50: calc(clamp(0, 50 - var(--wa-color-gray-key), 1) * 100%);
--wa-color-gray-gte-50: calc(100% - var(--wa-color-gray-lt-50));
--wa-color-gray-lt-60: calc(clamp(0, 60 - var(--wa-color-gray-key), 1) * 100%);
--wa-color-gray-gte-60: calc(100% - var(--wa-color-gray-lt-60));
--wa-color-gray-lt-70: calc(clamp(0, 70 - var(--wa-color-gray-key), 1) * 100%);
--wa-color-gray-gte-70: calc(100% - var(--wa-color-gray-lt-70));
--wa-color-gray-lt-80: calc(clamp(0, 80 - var(--wa-color-gray-key), 1) * 100%);
--wa-color-gray-gte-80: calc(100% - var(--wa-color-gray-lt-80));
/*
* Convenience tokens for common tint cutoffs
* --wa-color-{hue}-N-max ➡️ var(--color-{hue}) if key <= N, var(--color-{hue}-N) otherwise
* --wa-color-{hue}-N-min ➡️ var(--color-{hue}) if key >= N, var(--color-{hue}-N) otherwise
*/
--wa-color-red-max-40: color-mix(in oklab, var(--wa-color-red) var(--wa-color-red-lt-40), var(--wa-color-red-40));
--wa-color-red-min-40: color-mix(in oklab, var(--wa-color-red) var(--wa-color-red-gte-40), var(--wa-color-red-40));
--wa-color-red-max-50: color-mix(in oklab, var(--wa-color-red) var(--wa-color-red-lt-50), var(--wa-color-red-50));
--wa-color-red-min-50: color-mix(in oklab, var(--wa-color-red) var(--wa-color-red-gte-50), var(--wa-color-red-50));
--wa-color-red-max-60: color-mix(in oklab, var(--wa-color-red) var(--wa-color-red-lt-60), var(--wa-color-red-60));
--wa-color-red-min-60: color-mix(in oklab, var(--wa-color-red) var(--wa-color-red-gte-60), var(--wa-color-red-60));
--wa-color-red-max-70: color-mix(in oklab, var(--wa-color-red) var(--wa-color-red-lt-70), var(--wa-color-red-70));
--wa-color-red-min-70: color-mix(in oklab, var(--wa-color-red) var(--wa-color-red-gte-70), var(--wa-color-red-70));
--wa-color-yellow-max-40: color-mix(
in oklab,
var(--wa-color-yellow) var(--wa-color-yellow-lt-40),
var(--wa-color-yellow-40)
);
--wa-color-yellow-min-40: color-mix(
in oklab,
var(--wa-color-yellow) var(--wa-color-yellow-gte-40),
var(--wa-color-yellow-40)
);
--wa-color-yellow-max-50: color-mix(
in oklab,
var(--wa-color-yellow) var(--wa-color-yellow-lt-50),
var(--wa-color-yellow-50)
);
--wa-color-yellow-min-50: color-mix(
in oklab,
var(--wa-color-yellow) var(--wa-color-yellow-gte-50),
var(--wa-color-yellow-50)
);
--wa-color-yellow-max-60: color-mix(
in oklab,
var(--wa-color-yellow) var(--wa-color-yellow-lt-60),
var(--wa-color-yellow-60)
);
--wa-color-yellow-min-60: color-mix(
in oklab,
var(--wa-color-yellow) var(--wa-color-yellow-gte-60),
var(--wa-color-yellow-60)
);
--wa-color-yellow-max-70: color-mix(
in oklab,
var(--wa-color-yellow) var(--wa-color-yellow-lt-70),
var(--wa-color-yellow-70)
);
--wa-color-yellow-min-70: color-mix(
in oklab,
var(--wa-color-yellow) var(--wa-color-yellow-gte-70),
var(--wa-color-yellow-70)
);
--wa-color-green-max-40: color-mix(
in oklab,
var(--wa-color-green) var(--wa-color-green-lt-40),
var(--wa-color-green-40)
);
--wa-color-green-min-40: color-mix(
in oklab,
var(--wa-color-green) var(--wa-color-green-gte-40),
var(--wa-color-green-40)
);
--wa-color-green-max-50: color-mix(
in oklab,
var(--wa-color-green) var(--wa-color-green-lt-50),
var(--wa-color-green-50)
);
--wa-color-green-min-50: color-mix(
in oklab,
var(--wa-color-green) var(--wa-color-green-gte-50),
var(--wa-color-green-50)
);
--wa-color-green-max-60: color-mix(
in oklab,
var(--wa-color-green) var(--wa-color-green-lt-60),
var(--wa-color-green-60)
);
--wa-color-green-min-60: color-mix(
in oklab,
var(--wa-color-green) var(--wa-color-green-gte-60),
var(--wa-color-green-60)
);
--wa-color-green-max-70: color-mix(
in oklab,
var(--wa-color-green) var(--wa-color-green-lt-70),
var(--wa-color-green-70)
);
--wa-color-green-min-70: color-mix(
in oklab,
var(--wa-color-green) var(--wa-color-green-gte-70),
var(--wa-color-green-70)
);
--wa-color-cyan-max-40: color-mix(in oklab, var(--wa-color-cyan) var(--wa-color-cyan-lt-40), var(--wa-color-cyan-40));
--wa-color-cyan-min-40: color-mix(
in oklab,
var(--wa-color-cyan) var(--wa-color-cyan-gte-40),
var(--wa-color-cyan-40)
);
--wa-color-cyan-max-50: color-mix(in oklab, var(--wa-color-cyan) var(--wa-color-cyan-lt-50), var(--wa-color-cyan-50));
--wa-color-cyan-min-50: color-mix(
in oklab,
var(--wa-color-cyan) var(--wa-color-cyan-gte-50),
var(--wa-color-cyan-50)
);
--wa-color-cyan-max-60: color-mix(in oklab, var(--wa-color-cyan) var(--wa-color-cyan-lt-60), var(--wa-color-cyan-60));
--wa-color-cyan-min-60: color-mix(
in oklab,
var(--wa-color-cyan) var(--wa-color-cyan-gte-60),
var(--wa-color-cyan-60)
);
--wa-color-cyan-max-70: color-mix(in oklab, var(--wa-color-cyan) var(--wa-color-cyan-lt-70), var(--wa-color-cyan-70));
--wa-color-cyan-min-70: color-mix(
in oklab,
var(--wa-color-cyan) var(--wa-color-cyan-gte-70),
var(--wa-color-cyan-70)
);
--wa-color-blue-max-40: color-mix(in oklab, var(--wa-color-blue) var(--wa-color-blue-lt-40), var(--wa-color-blue-40));
--wa-color-blue-min-40: color-mix(
in oklab,
var(--wa-color-blue) var(--wa-color-blue-gte-40),
var(--wa-color-blue-40)
);
--wa-color-blue-max-50: color-mix(in oklab, var(--wa-color-blue) var(--wa-color-blue-lt-50), var(--wa-color-blue-50));
--wa-color-blue-min-50: color-mix(
in oklab,
var(--wa-color-blue) var(--wa-color-blue-gte-50),
var(--wa-color-blue-50)
);
--wa-color-blue-max-60: color-mix(in oklab, var(--wa-color-blue) var(--wa-color-blue-lt-60), var(--wa-color-blue-60));
--wa-color-blue-min-60: color-mix(
in oklab,
var(--wa-color-blue) var(--wa-color-blue-gte-60),
var(--wa-color-blue-60)
);
--wa-color-blue-max-70: color-mix(in oklab, var(--wa-color-blue) var(--wa-color-blue-lt-70), var(--wa-color-blue-70));
--wa-color-blue-min-70: color-mix(
in oklab,
var(--wa-color-blue) var(--wa-color-blue-gte-70),
var(--wa-color-blue-70)
);
--wa-color-indigo-max-40: color-mix(
in oklab,
var(--wa-color-indigo) var(--wa-color-indigo-lt-40),
var(--wa-color-indigo-40)
);
--wa-color-indigo-min-40: color-mix(
in oklab,
var(--wa-color-indigo) var(--wa-color-indigo-gte-40),
var(--wa-color-indigo-40)
);
--wa-color-indigo-max-50: color-mix(
in oklab,
var(--wa-color-indigo) var(--wa-color-indigo-lt-50),
var(--wa-color-indigo-50)
);
--wa-color-indigo-min-50: color-mix(
in oklab,
var(--wa-color-indigo) var(--wa-color-indigo-gte-50),
var(--wa-color-indigo-50)
);
--wa-color-indigo-max-60: color-mix(
in oklab,
var(--wa-color-indigo) var(--wa-color-indigo-lt-60),
var(--wa-color-indigo-60)
);
--wa-color-indigo-min-60: color-mix(
in oklab,
var(--wa-color-indigo) var(--wa-color-indigo-gte-60),
var(--wa-color-indigo-60)
);
--wa-color-indigo-max-70: color-mix(
in oklab,
var(--wa-color-indigo) var(--wa-color-indigo-lt-70),
var(--wa-color-indigo-70)
);
--wa-color-indigo-min-70: color-mix(
in oklab,
var(--wa-color-indigo) var(--wa-color-indigo-gte-70),
var(--wa-color-indigo-70)
);
--wa-color-purple-max-40: color-mix(
in oklab,
var(--wa-color-purple) var(--wa-color-purple-lt-40),
var(--wa-color-purple-40)
);
--wa-color-purple-min-40: color-mix(
in oklab,
var(--wa-color-purple) var(--wa-color-purple-gte-40),
var(--wa-color-purple-40)
);
--wa-color-purple-max-50: color-mix(
in oklab,
var(--wa-color-purple) var(--wa-color-purple-lt-50),
var(--wa-color-purple-50)
);
--wa-color-purple-min-50: color-mix(
in oklab,
var(--wa-color-purple) var(--wa-color-purple-gte-50),
var(--wa-color-purple-50)
);
--wa-color-purple-max-60: color-mix(
in oklab,
var(--wa-color-purple) var(--wa-color-purple-lt-60),
var(--wa-color-purple-60)
);
--wa-color-purple-min-60: color-mix(
in oklab,
var(--wa-color-purple) var(--wa-color-purple-gte-60),
var(--wa-color-purple-60)
);
--wa-color-purple-max-70: color-mix(
in oklab,
var(--wa-color-purple) var(--wa-color-purple-lt-70),
var(--wa-color-purple-70)
);
--wa-color-purple-min-70: color-mix(
in oklab,
var(--wa-color-purple) var(--wa-color-purple-gte-70),
var(--wa-color-purple-70)
);
--wa-color-pink-max-40: color-mix(in oklab, var(--wa-color-pink) var(--wa-color-pink-lt-40), var(--wa-color-pink-40));
--wa-color-pink-min-40: color-mix(
in oklab,
var(--wa-color-pink) var(--wa-color-pink-gte-40),
var(--wa-color-pink-40)
);
--wa-color-pink-max-50: color-mix(in oklab, var(--wa-color-pink) var(--wa-color-pink-lt-50), var(--wa-color-pink-50));
--wa-color-pink-min-50: color-mix(
in oklab,
var(--wa-color-pink) var(--wa-color-pink-gte-50),
var(--wa-color-pink-50)
);
--wa-color-pink-max-60: color-mix(in oklab, var(--wa-color-pink) var(--wa-color-pink-lt-60), var(--wa-color-pink-60));
--wa-color-pink-min-60: color-mix(
in oklab,
var(--wa-color-pink) var(--wa-color-pink-gte-60),
var(--wa-color-pink-60)
);
--wa-color-pink-max-70: color-mix(in oklab, var(--wa-color-pink) var(--wa-color-pink-lt-70), var(--wa-color-pink-70));
--wa-color-pink-min-70: color-mix(
in oklab,
var(--wa-color-pink) var(--wa-color-pink-gte-70),
var(--wa-color-pink-70)
);
--wa-color-gray-max-40: color-mix(in oklab, var(--wa-color-gray) var(--wa-color-gray-lt-40), var(--wa-color-gray-40));
--wa-color-gray-min-40: color-mix(
in oklab,
var(--wa-color-gray) var(--wa-color-gray-gte-40),
var(--wa-color-gray-40)
);
--wa-color-gray-max-50: color-mix(in oklab, var(--wa-color-gray) var(--wa-color-gray-lt-50), var(--wa-color-gray-50));
--wa-color-gray-min-50: color-mix(
in oklab,
var(--wa-color-gray) var(--wa-color-gray-gte-50),
var(--wa-color-gray-50)
);
--wa-color-gray-max-60: color-mix(in oklab, var(--wa-color-gray) var(--wa-color-gray-lt-60), var(--wa-color-gray-60));
--wa-color-gray-min-60: color-mix(
in oklab,
var(--wa-color-gray) var(--wa-color-gray-gte-60),
var(--wa-color-gray-60)
);
--wa-color-gray-max-70: color-mix(in oklab, var(--wa-color-gray) var(--wa-color-gray-lt-70), var(--wa-color-gray-70));
--wa-color-gray-min-70: color-mix(
in oklab,
var(--wa-color-gray) var(--wa-color-gray-gte-70),
var(--wa-color-gray-70)
);
/* Text color: white if key < 60, {hue}-10 otherwise */
--wa-color-red-on: color-mix(in oklab, var(--wa-color-red-10) var(--wa-color-red-gte-60), white);
--wa-color-yellow-on: color-mix(in oklab, var(--wa-color-yellow-10) var(--wa-color-yellow-gte-60), white);
--wa-color-green-on: color-mix(in oklab, var(--wa-color-green-10) var(--wa-color-green-gte-60), white);
--wa-color-cyan-on: color-mix(in oklab, var(--wa-color-cyan-10) var(--wa-color-cyan-gte-60), white);
--wa-color-blue-on: color-mix(in oklab, var(--wa-color-blue-10) var(--wa-color-blue-gte-60), white);
--wa-color-indigo-on: color-mix(in oklab, var(--wa-color-indigo-10) var(--wa-color-indigo-gte-60), white);
--wa-color-purple-on: color-mix(in oklab, var(--wa-color-purple-10) var(--wa-color-purple-gte-60), white);
--wa-color-pink-on: color-mix(in oklab, var(--wa-color-pink-10) var(--wa-color-pink-gte-60), white);
--wa-color-gray-on: color-mix(in oklab, var(--wa-color-gray-10) var(--wa-color-gray-gte-60), white);
}

View File

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

View File

@@ -1,3 +1,5 @@
@import url('base.css');
:where(:root),
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']),

View File

@@ -1,3 +1,5 @@
@import url('base.css');
:where(:root),
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']),

View File

@@ -1,3 +1,5 @@
@import url('base.css');
:where(:root),
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']),

View File

@@ -1,3 +1,5 @@
@import url('base.css');
:where(:root),
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']),

View File

@@ -1,3 +1,5 @@
@import url('base.css');
:where(:root),
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']),

View File

@@ -1,3 +1,5 @@
@import url('base.css');
:where(:root),
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']),

View File

@@ -1,3 +1,5 @@
@import url('base.css');
:where(:root),
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']),

View File

@@ -8,7 +8,9 @@ import fs from 'fs';
import path from 'path';
import { PALETTE_DIR } from './util.js';
export const paletteFiles = fs.readdirSync(PALETTE_DIR + '/').filter(file => file.endsWith('.css'));
export const paletteFiles = fs
.readdirSync(PALETTE_DIR + '/')
.filter(file => file.endsWith('.css') && !file.endsWith('base.css'));
export const declarationRegex =
/^\s*--wa-color-(?<hue>[a-z]+)-(?<level>[0-9]+):\s*(?<color>.+?)\s*(\/\*.+?\*\/)?\s*;$/gm;
export const rawCSS = {};

View File

@@ -1,98 +1,207 @@
// Run via node ranges.js to analyze all palettes
// or node ranges.js <paletteId> to analyze a single palette
// Add palette ids, hue names, or tints to filter the analysis (e.g. node ranges.js red anodized -gray)
import palettes from './palettes-analyzed.js';
import { toPrecision } from './util.js';
let paletteId = process.argv[2];
import { aggregates, normalizeAngles, toPrecision } from './util.js';
/**
* Each "test" consists of the following params to analyze:
* - component: The color component to analyze (h, c, l)
* - label: The label to display in the console
* - by: The grouping to analyze by (tint, hue)
* - levels: The number of tints from the core color to include in the analysis.
* Examples: undefined for all tints, 0 for the core color only, 10 for the core color and ±10 from it.
* - component: The color component to analyze (h, c, l). If `getValue()` is provided, this is ignored.
* - getValue: A function to extract the value to analyze from a color, for more complex analysis than just getting a component
* - by: The grouping to analyze by (1-2 of 'tint', 'hue', 'palette'). If `getKey()` is provided, this is ignored
* - getKey: A function to generate a key for each group. If not provided, it is generated based on the 'by' param
* - caption: The caption to display in the console. If not provided, a default label is generated from test params.
* - filter: Restrict to specific hues/tints/palettes or exclude them
* - stats: The stats to calculate for each group (min, max, mid, extent, avg, median, count)
*/
let tests = [
{ component: 'h', label: 'Hue', by: 'hue', levels: paletteId ? undefined : 10 },
{ component: 'c', label: 'Chroma', by: 'tint' },
{ component: 'l', label: 'L', by: 'tint' },
{ component: 'h', by: 'hue', filter: ['core ± 10', '-gray'] },
{ component: 'h', by: 'hue', filter: ['core', '-gray'] },
{ component: 'h', by: 'palette', filter: ['core', 'gray'] },
{ component: 'c', by: 'tint', filter: '-gray' },
{ component: 'c', by: 'palette', filter: 'core', stats: ['max', 'avg', 'median', 'count'] },
{ component: 'l', by: 'tint' },
{
caption: 'Hue change between consecutive tints',
getValue(color, { palette, tint, hue }) {
let nextTint = getNextTint(tint);
let nextColor = palettes[palette][hue][nextTint];
return color.h - nextColor.h;
},
getKey({ tint }) {
return `${tint}${getNextTint(tint)}`;
},
filter: '-95',
},
];
if (!paletteId) {
tests.push({ component: 'h', label: 'Core Hue', by: 'hue', levels: 0 });
const COMPONENT_NAMES = { l: 'lightness', c: 'chroma', h: 'hue' };
const CORE_TINT_MICROSYNTAX = /^core\s*((?<op>[-+±])\s*(?<offset>\d+))?$/;
const all = {
tints: ['95', '90', '80', '70', '60', '50', '40', '30', '20', '10', '05'],
hues: ['red', 'yellow', 'green', 'cyan', 'blue', 'indigo', 'purple', 'pink', 'gray'],
palettes: Object.keys(palettes),
};
let args = process.argv.slice(2);
let used = getSubset(all, args);
const getNextTint = tint => Number(tint) + (tint == 5 || tint == 90 ? 5 : 10);
for (let key in used) {
if (used[key].length < all[key].length) {
// Subset of values
console.log(`Analyzing only ${key}:`, used[key].join(', '));
}
}
const tints = ['95', '90', '80', '70', '60', '50', '40', '30', '20', '10', '05'];
const hues = ['red', 'yellow', 'green', 'cyan', 'blue', 'indigo', 'purple', 'pink', 'gray'];
/**
* Apply a list of args (hues, tints, palette ids) to add or exclude against the corresponding arrays
*/
function getSubset(all, args) {
args = Array.isArray(args) ? args : [args];
function analyzePalette(scales, results, { component, levels, by = 'tint' }) {
for (let hue in scales) {
let colors = scales[hue];
let key = colors.maxChromaTint;
let resultsByHue = by === 'hue' ? results[hue] : results;
let used = {
tints: [],
hues: [],
palettes: [],
};
for (let tint of tints) {
let color = colors[tint];
let value = color[component];
let resultsByTint = by === 'tint' ? resultsByHue[tint] : resultsByHue;
if (args.length > 0) {
for (let arg of args) {
let isNegative = arg.startsWith('-');
arg = isNegative ? arg.slice(1) : arg;
let key = Object.entries(all).find(([key, values]) => values.includes(arg))?.[0];
if (levels === undefined || Math.abs(tint - key) <= levels) {
if (resultsByTint.min > value) resultsByTint.min = value;
if (resultsByTint.max < value) resultsByTint.max = value;
if (!key && CORE_TINT_MICROSYNTAX.test(arg)) {
key = 'tints';
}
}
}
}
function analyze(options = {}) {
let results = {};
let keys = options.by === 'hue' ? hues : tints;
for (let key of keys) {
results[key] = { min: Infinity, max: -Infinity };
}
if (paletteId) {
analyzePalette(palettes[paletteId], results, options);
} else {
for (let paletteId in palettes) {
analyzePalette(palettes[paletteId], results, options);
}
}
// Add extent & mid, make numbers easier to read
for (let key of keys) {
let info = results[key];
if (options.component === 'h') {
// Fixup hues crossing 0
if (Math.abs(info.max - info.min) > 180) {
info.min += 360;
if (info.min > info.max) {
[info.min, info.max] = [info.max, info.min];
if (key) {
if (isNegative) {
let array = used[key].length === 0 ? all[key] : used[key];
used[key] = array.filter(value => value !== arg);
} else {
used[key].push(arg);
}
}
}
}
info.extent = info.max - info.min;
info.mid = (info.min + info.max) / 2;
for (let prop in info) {
info[prop] = toPrecision(info[prop]);
// If no filters, use all values
for (let key in used) {
if (used[key].length === 0) {
used[key] = all[key].slice();
}
}
let label = `${options.label || options.component} ranges`;
console.log(label + (options.levels !== undefined ? `${options.levels} from core tint)` : '') + ':');
console.table(results);
return used;
}
if (paletteId) {
// Analyze a single palette
console.log(`Analyzing palette '${paletteId}'`);
function runTest(test = {}) {
let {
component,
getValue = color => color[component],
by = 'tint',
getKey = getDefaultKey(by),
filter,
caption = getDefaultCaption(test),
stats = ['min', 'max', 'median', 'count'],
silent,
} = test;
let results = {};
let localUsed = filter ? getSubset(used, filter) : used;
for (let palette of localUsed.palettes) {
for (let hue of localUsed.hues) {
let coreTint = palettes[palette][hue].maxChromaTint;
// Resolve any core tint microsyntax
let tints = localUsed.tints.flatMap(tint => {
if (CORE_TINT_MICROSYNTAX.test(tint)) {
let { op, offset } = CORE_TINT_MICROSYNTAX.exec(tint).groups;
if (!offset) {
return coreTint;
}
return used.tints.filter(t => {
let distance = t - coreTint;
return Math.abs(distance) <= offset && !((op === '-' && distance > 0) || (op === '+' && distance < 0));
});
}
return tint;
});
for (let tint of tints) {
let key = getKey({ hue, tint, palette });
let color = palettes[palette][hue][tint];
let value = getValue(color, { hue, tint, palette }, localUsed);
results[key] ??= [];
results[key].push(value);
}
}
}
// Process results
for (let key in results) {
let values = results[key];
if (component === 'h') {
values = normalizeAngles(values);
}
results[key] = stats.reduce((acc, stat) => {
acc[stat] = toPrecision(aggregates[stat](values, acc));
return acc;
}, {});
}
if (!silent) {
console.log(caption);
console.table(results);
console.log('\n');
}
return results;
}
for (let test of tests) {
analyze(test);
runTest(test);
}
function getDefaultKey(by) {
by = Array.isArray(by) ? by : [by];
return variables =>
by
.map((variableName, i) => {
if (variableName === 'tint' && i === 0) {
// Drop leading zeros because they throw off row order
return String(variables.tint).replace(/^0+/, '');
}
return variables[variableName];
})
.join('-');
}
function getDefaultCaption({ component, by, filter }) {
let ret = COMPONENT_NAMES[component];
by = Array.isArray(by) ? by : [by];
ret += ` by ${by.join(' ')}`;
if (filter) {
filter = Array.isArray(filter) ? filter.join(', ') : filter;
ret += ` (${filter})`;
}
ret = ret.replace('hue by hue', 'hue by color name');
return capitalize(ret);
}
function capitalize(str) {
return str[0].toUpperCase() + str.slice(1);
}

View File

@@ -6,7 +6,7 @@
import chalk from 'chalk';
import fs from 'fs';
import path from 'path';
import palettes, { rawPalettes } from './palettes-analyzed.js';
import palettes from './palettes-analyzed.js';
import { PALETTE_DIR, formatComparison, hueToChalk } from './util.js';
const selector = paletteId =>

View File

@@ -57,3 +57,50 @@ export function hueToChalk(hue) {
export function toPrecision(value, precision = 2) {
return +Number(value).toPrecision(precision);
}
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 const aggregates = {
min: values => Math.min(...values),
max: values => Math.max(...values),
avg: values => values.reduce((a, b) => a + b, 0) / values.length,
count: values => values.length,
values: values => values,
median: values => {
let sorted = values.slice().sort((a, b) => a - b);
let mid = Math.floor(sorted.length / 2);
return sorted.length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
},
extent: (values, { min, max }) => max - min,
mid: (values, { min, max }) => (max + min) / 2,
};

View File

@@ -1,3 +1,5 @@
@import url('base.css');
:where(:root),
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']),

9
src/styles/data.js Normal file
View File

@@ -0,0 +1,9 @@
/**
* Any data relating to the design system.
*/
export const hues = ['red', 'yellow', 'green', 'cyan', 'blue', 'indigo', 'purple', 'pink', 'gray'];
export const tints = ['05', '10', '20', '30', '40', '50', '60', '70', '80', '90', '95'];
export const variants = ['neutral', 'brand', 'success', 'warning', 'danger'];
export const roles = ['fill', 'border', 'on'];
export const attention_levels = ['loud', 'normal', 'quiet'];
export const semantic_color_types = roles.map(layer => attention_levels.map(priority => layer + '-' + priority)).flat();

View File

@@ -26,13 +26,13 @@
*/
--wa-color-brand-fill-quiet: var(--wa-color-brand-95);
--wa-color-brand-fill-normal: var(--wa-color-brand-90);
--wa-color-brand-fill-loud: var(--wa-color-brand-80);
--wa-color-brand-fill-loud: var(--wa-color-brand);
--wa-color-brand-border-quiet: var(--wa-color-brand-90);
--wa-color-brand-border-normal: var(--wa-color-brand-80);
--wa-color-brand-border-loud: var(--wa-color-brand-70);
--wa-color-brand-on-quiet: var(--wa-color-brand-40);
--wa-color-brand-on-normal: var(--wa-color-brand-30);
--wa-color-brand-on-loud: black;
--wa-color-brand-on-loud: var(--wa-color-brand-on);
--wa-color-success-fill-quiet: var(--wa-color-green-95);
--wa-color-success-fill-normal: var(--wa-color-green-90);
@@ -90,13 +90,13 @@
*/
--wa-color-brand-fill-quiet: var(--wa-color-brand-10);
--wa-color-brand-fill-normal: var(--wa-color-brand-20);
--wa-color-brand-fill-loud: var(--wa-color-brand-80);
--wa-color-brand-fill-loud: var(--wa-color-brand);
--wa-color-brand-border-quiet: var(--wa-color-brand-20);
--wa-color-brand-border-normal: var(--wa-color-brand-30);
--wa-color-brand-border-loud: var(--wa-color-brand-60);
--wa-color-brand-on-quiet: var(--wa-color-brand-80);
--wa-color-brand-on-normal: var(--wa-color-brand-90);
--wa-color-brand-on-loud: black;
--wa-color-brand-on-loud: var(--wa-color-brand-on);
--wa-color-success-fill-quiet: var(--wa-color-green-10);
--wa-color-success-fill-normal: var(--wa-color-green-20);

View File

@@ -17,7 +17,7 @@
*/
--wa-color-brand-fill-quiet: var(--wa-color-brand-95);
--wa-color-brand-fill-normal: var(--wa-color-brand-90);
--wa-color-brand-fill-loud: var(--wa-color-brand-70);
--wa-color-brand-fill-loud: var(--wa-color-brand-min-70);
--wa-color-brand-border-quiet: var(--wa-color-brand-70);
--wa-color-brand-border-normal: var(--wa-color-brand-50);
--wa-color-brand-border-loud: var(--wa-color-brand-30);
@@ -77,7 +77,7 @@
*/
--wa-color-brand-fill-quiet: var(--wa-color-brand-10);
--wa-color-brand-fill-normal: var(--wa-color-brand-20);
--wa-color-brand-fill-loud: var(--wa-color-brand-50);
--wa-color-brand-fill-loud: var(--wa-color-brand-max-50);
--wa-color-brand-border-quiet: var(--wa-color-brand-30);
--wa-color-brand-border-normal: var(--wa-color-brand-50);
--wa-color-brand-border-loud: var(--wa-color-brand-80);

View File

@@ -24,7 +24,7 @@
*/
--wa-color-brand-fill-quiet: var(--wa-color-brand-95);
--wa-color-brand-fill-normal: var(--wa-color-brand-90);
--wa-color-brand-fill-loud: var(--wa-color-brand-50);
--wa-color-brand-fill-loud: var(--wa-color-brand-max-50);
--wa-color-brand-border-quiet: var(--wa-color-brand-80);
--wa-color-brand-border-normal: var(--wa-color-brand-60);
--wa-color-brand-border-loud: var(--wa-color-brand-40);
@@ -96,7 +96,7 @@
*/
--wa-color-brand-fill-quiet: var(--wa-color-brand-20);
--wa-color-brand-fill-normal: var(--wa-color-brand-30);
--wa-color-brand-fill-loud: var(--wa-color-brand-70);
--wa-color-brand-fill-loud: var(--wa-color-brand-min-70);
--wa-color-brand-border-quiet: var(--wa-color-brand-30);
--wa-color-brand-border-normal: var(--wa-color-brand-40);
--wa-color-brand-border-loud: var(--wa-color-brand-80);

View File

@@ -1,3 +1,4 @@
@import url('scales.css');
@import url('../../brand/blue.css');
/**
@@ -20,25 +21,25 @@
* Surface colors help convey hierarchy through elevation, where raised is closest to the user and lowered is farthest away. */
--wa-color-surface-raised: white;
--wa-color-surface-default: white;
--wa-color-surface-lowered: var(--wa-color-gray-95);
--wa-color-surface-border: var(--wa-color-gray-90);
--wa-color-surface-lowered: var(--wa-color-neutral-95);
--wa-color-surface-border: var(--wa-color-neutral-90);
/* Text colors are used for standard text elements.
* Recommended: minimum 4.5:1 contrast ratio between text colors and surface colors. */
--wa-color-text-normal: var(--wa-color-gray-10);
--wa-color-text-quiet: var(--wa-color-gray-40);
--wa-color-text-normal: var(--wa-color-neutral-10);
--wa-color-text-quiet: var(--wa-color-neutral-40);
--wa-color-text-link: var(--wa-color-brand-40);
/* Overlays provide a backdrop for isolated content, often allowing background context to show through. */
--wa-color-overlay-modal: color-mix(in oklab, var(--wa-color-gray-05) 50%, transparent);
--wa-color-overlay-inline: color-mix(in oklab, var(--wa-color-gray-80) 20%, transparent);
--wa-color-overlay-modal: color-mix(in oklab, var(--wa-color-neutral-05) 50%, transparent);
--wa-color-overlay-inline: color-mix(in oklab, var(--wa-color-neutral-80) 20%, transparent);
/* Shadows indicate elevation. Shadow color is used in your theme's shadow properties.
* By default, the opacity of your shadow color is tied to the blur of shadows in your theme.
* Because solid shadows appear stronger in color than diffused shadows, this helps keep consistent color intensity. */
--wa-color-shadow: color-mix(
in oklab,
var(--wa-color-gray-05) calc(var(--wa-shadow-blur-scale) * 4% + 8%),
var(--wa-color-neutral-05) calc(var(--wa-shadow-blur-scale) * 4% + 8%),
transparent
);
@@ -61,7 +62,7 @@
*/
--wa-color-brand-fill-quiet: var(--wa-color-brand-95);
--wa-color-brand-fill-normal: var(--wa-color-brand-90);
--wa-color-brand-fill-loud: var(--wa-color-brand-50);
--wa-color-brand-fill-loud: var(--wa-color-brand-max-50);
--wa-color-brand-border-quiet: var(--wa-color-brand-90);
--wa-color-brand-border-normal: var(--wa-color-brand-80);
--wa-color-brand-border-loud: var(--wa-color-brand-60);
@@ -69,14 +70,14 @@
--wa-color-brand-on-normal: var(--wa-color-brand-30);
--wa-color-brand-on-loud: white;
--wa-color-success-fill-quiet: var(--wa-color-green-95);
--wa-color-success-fill-normal: var(--wa-color-green-90);
--wa-color-success-fill-loud: var(--wa-color-green-50);
--wa-color-success-border-quiet: var(--wa-color-green-90);
--wa-color-success-border-normal: var(--wa-color-green-80);
--wa-color-success-border-loud: var(--wa-color-green-60);
--wa-color-success-on-quiet: var(--wa-color-green-40);
--wa-color-success-on-normal: var(--wa-color-green-30);
--wa-color-success-fill-quiet: var(--wa-color-success-95);
--wa-color-success-fill-normal: var(--wa-color-success-90);
--wa-color-success-fill-loud: var(--wa-color-success-50);
--wa-color-success-border-quiet: var(--wa-color-success-90);
--wa-color-success-border-normal: var(--wa-color-success-80);
--wa-color-success-border-loud: var(--wa-color-success-60);
--wa-color-success-on-quiet: var(--wa-color-success-40);
--wa-color-success-on-normal: var(--wa-color-success-30);
--wa-color-success-on-loud: white;
--wa-color-warning-fill-quiet: var(--wa-color-yellow-95);
@@ -89,24 +90,24 @@
--wa-color-warning-on-normal: var(--wa-color-yellow-30);
--wa-color-warning-on-loud: white;
--wa-color-danger-fill-quiet: var(--wa-color-red-95);
--wa-color-danger-fill-normal: var(--wa-color-red-90);
--wa-color-danger-fill-loud: var(--wa-color-red-50);
--wa-color-danger-border-quiet: var(--wa-color-red-90);
--wa-color-danger-border-normal: var(--wa-color-red-80);
--wa-color-danger-border-loud: var(--wa-color-red-60);
--wa-color-danger-on-quiet: var(--wa-color-red-40);
--wa-color-danger-on-normal: var(--wa-color-red-30);
--wa-color-danger-fill-quiet: var(--wa-color-danger-95);
--wa-color-danger-fill-normal: var(--wa-color-danger-90);
--wa-color-danger-fill-loud: var(--wa-color-danger-50);
--wa-color-danger-border-quiet: var(--wa-color-danger-90);
--wa-color-danger-border-normal: var(--wa-color-danger-80);
--wa-color-danger-border-loud: var(--wa-color-danger-60);
--wa-color-danger-on-quiet: var(--wa-color-danger-40);
--wa-color-danger-on-normal: var(--wa-color-danger-30);
--wa-color-danger-on-loud: white;
--wa-color-neutral-fill-quiet: var(--wa-color-gray-95);
--wa-color-neutral-fill-normal: var(--wa-color-gray-90);
--wa-color-neutral-fill-loud: var(--wa-color-gray-20);
--wa-color-neutral-border-quiet: var(--wa-color-gray-90);
--wa-color-neutral-border-normal: var(--wa-color-gray-80);
--wa-color-neutral-border-loud: var(--wa-color-gray-60);
--wa-color-neutral-on-quiet: var(--wa-color-gray-40);
--wa-color-neutral-on-normal: var(--wa-color-gray-30);
--wa-color-neutral-fill-quiet: var(--wa-color-neutral-95);
--wa-color-neutral-fill-normal: var(--wa-color-neutral-90);
--wa-color-neutral-fill-loud: var(--wa-color-neutral-20);
--wa-color-neutral-border-quiet: var(--wa-color-neutral-90);
--wa-color-neutral-border-normal: var(--wa-color-neutral-80);
--wa-color-neutral-border-loud: var(--wa-color-neutral-60);
--wa-color-neutral-on-quiet: var(--wa-color-neutral-40);
--wa-color-neutral-on-normal: var(--wa-color-neutral-30);
--wa-color-neutral-on-loud: white;
}
@@ -120,17 +121,17 @@
/**
* Foundational Colors
*/
--wa-color-surface-raised: var(--wa-color-gray-10);
--wa-color-surface-default: var(--wa-color-gray-05);
--wa-color-surface-raised: var(--wa-color-neutral-10);
--wa-color-surface-default: var(--wa-color-neutral-05);
--wa-color-surface-lowered: color-mix(in oklab, var(--wa-color-surface-default), black 20%);
--wa-color-surface-border: var(--wa-color-gray-20);
--wa-color-surface-border: var(--wa-color-neutral-20);
--wa-color-text-normal: var(--wa-color-gray-95);
--wa-color-text-quiet: var(--wa-color-gray-60);
--wa-color-text-normal: var(--wa-color-neutral-95);
--wa-color-text-quiet: var(--wa-color-neutral-60);
--wa-color-text-link: var(--wa-color-brand-70);
--wa-color-overlay-modal: color-mix(in oklab, black 60%, transparent);
--wa-color-overlay-inline: color-mix(in oklab, var(--wa-color-gray-50) 10%, transparent);
--wa-color-overlay-inline: color-mix(in oklab, var(--wa-color-neutral-50) 10%, transparent);
/* Mixing with --wa-color-surface-lowered prevents shadows from becoming excessively dark relative to --wa-color-surface-default. */
--wa-color-shadow: color-mix(
@@ -149,7 +150,7 @@
*/
--wa-color-brand-fill-quiet: var(--wa-color-brand-10);
--wa-color-brand-fill-normal: var(--wa-color-brand-20);
--wa-color-brand-fill-loud: var(--wa-color-brand-50);
--wa-color-brand-fill-loud: var(--wa-color-brand-max-50);
--wa-color-brand-border-quiet: var(--wa-color-brand-20);
--wa-color-brand-border-normal: var(--wa-color-brand-30);
--wa-color-brand-border-loud: var(--wa-color-brand-40);
@@ -157,14 +158,14 @@
--wa-color-brand-on-normal: var(--wa-color-brand-70);
--wa-color-brand-on-loud: white;
--wa-color-success-fill-quiet: var(--wa-color-green-10);
--wa-color-success-fill-normal: var(--wa-color-green-20);
--wa-color-success-fill-loud: var(--wa-color-green-50);
--wa-color-success-border-quiet: var(--wa-color-green-20);
--wa-color-success-border-normal: var(--wa-color-green-30);
--wa-color-success-border-loud: var(--wa-color-green-40);
--wa-color-success-on-quiet: var(--wa-color-green-60);
--wa-color-success-on-normal: var(--wa-color-green-70);
--wa-color-success-fill-quiet: var(--wa-color-success-10);
--wa-color-success-fill-normal: var(--wa-color-success-20);
--wa-color-success-fill-loud: var(--wa-color-success-50);
--wa-color-success-border-quiet: var(--wa-color-success-20);
--wa-color-success-border-normal: var(--wa-color-success-30);
--wa-color-success-border-loud: var(--wa-color-success-40);
--wa-color-success-on-quiet: var(--wa-color-success-60);
--wa-color-success-on-normal: var(--wa-color-success-70);
--wa-color-success-on-loud: white;
--wa-color-warning-fill-quiet: var(--wa-color-yellow-10);
@@ -177,23 +178,23 @@
--wa-color-warning-on-normal: var(--wa-color-yellow-70);
--wa-color-warning-on-loud: white;
--wa-color-danger-fill-quiet: var(--wa-color-red-10);
--wa-color-danger-fill-normal: var(--wa-color-red-20);
--wa-color-danger-fill-loud: var(--wa-color-red-50);
--wa-color-danger-border-quiet: var(--wa-color-red-20);
--wa-color-danger-border-normal: var(--wa-color-red-30);
--wa-color-danger-border-loud: var(--wa-color-red-40);
--wa-color-danger-on-quiet: var(--wa-color-red-60);
--wa-color-danger-on-normal: var(--wa-color-red-70);
--wa-color-danger-fill-quiet: var(--wa-color-danger-10);
--wa-color-danger-fill-normal: var(--wa-color-danger-20);
--wa-color-danger-fill-loud: var(--wa-color-danger-50);
--wa-color-danger-border-quiet: var(--wa-color-danger-20);
--wa-color-danger-border-normal: var(--wa-color-danger-30);
--wa-color-danger-border-loud: var(--wa-color-danger-40);
--wa-color-danger-on-quiet: var(--wa-color-danger-60);
--wa-color-danger-on-normal: var(--wa-color-danger-70);
--wa-color-danger-on-loud: white;
--wa-color-neutral-fill-quiet: var(--wa-color-gray-10);
--wa-color-neutral-fill-normal: var(--wa-color-gray-20);
--wa-color-neutral-fill-loud: var(--wa-color-gray-90);
--wa-color-neutral-border-quiet: var(--wa-color-gray-20);
--wa-color-neutral-border-normal: var(--wa-color-gray-30);
--wa-color-neutral-border-loud: var(--wa-color-gray-40);
--wa-color-neutral-on-quiet: var(--wa-color-gray-60);
--wa-color-neutral-on-normal: var(--wa-color-gray-70);
--wa-color-neutral-on-loud: var(--wa-color-gray-05);
--wa-color-neutral-fill-quiet: var(--wa-color-neutral-10);
--wa-color-neutral-fill-normal: var(--wa-color-neutral-20);
--wa-color-neutral-fill-loud: var(--wa-color-neutral-90);
--wa-color-neutral-border-quiet: var(--wa-color-neutral-20);
--wa-color-neutral-border-normal: var(--wa-color-neutral-30);
--wa-color-neutral-border-loud: var(--wa-color-neutral-40);
--wa-color-neutral-on-quiet: var(--wa-color-neutral-60);
--wa-color-neutral-on-normal: var(--wa-color-neutral-70);
--wa-color-neutral-on-loud: var(--wa-color-neutral-05);
}

View File

@@ -0,0 +1,92 @@
/* DO NOT EDIT THIS FILE. It is generated from "scales.css.njk" */
:where(:root),
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']) {
--wa-color-success-05: var(--wa-color-green-05);
--wa-color-success-10: var(--wa-color-green-10);
--wa-color-success-20: var(--wa-color-green-20);
--wa-color-success-30: var(--wa-color-green-30);
--wa-color-success-40: var(--wa-color-green-40);
--wa-color-success-50: var(--wa-color-green-50);
--wa-color-success-60: var(--wa-color-green-60);
--wa-color-success-70: var(--wa-color-green-70);
--wa-color-success-80: var(--wa-color-green-80);
--wa-color-success-90: var(--wa-color-green-90);
--wa-color-success-95: var(--wa-color-green-95);
--wa-color-success: var(--wa-color-green);
--wa-color-success-max-40: var(--wa-color-max-green-40);
--wa-color-success-min-40: var(--wa-color-min-green-40);
--wa-color-success-max-50: var(--wa-color-max-green-50);
--wa-color-success-min-50: var(--wa-color-min-green-50);
--wa-color-success-max-60: var(--wa-color-max-green-60);
--wa-color-success-min-60: var(--wa-color-min-green-60);
--wa-color-success-max-70: var(--wa-color-max-green-70);
--wa-color-success-min-70: var(--wa-color-min-green-70);
--wa-color-warning-05: var(--wa-color-yellow-05);
--wa-color-warning-10: var(--wa-color-yellow-10);
--wa-color-warning-20: var(--wa-color-yellow-20);
--wa-color-warning-30: var(--wa-color-yellow-30);
--wa-color-warning-40: var(--wa-color-yellow-40);
--wa-color-warning-50: var(--wa-color-yellow-50);
--wa-color-warning-60: var(--wa-color-yellow-60);
--wa-color-warning-70: var(--wa-color-yellow-70);
--wa-color-warning-80: var(--wa-color-yellow-80);
--wa-color-warning-90: var(--wa-color-yellow-90);
--wa-color-warning-95: var(--wa-color-yellow-95);
--wa-color-warning: var(--wa-color-yellow);
--wa-color-warning-max-40: var(--wa-color-max-yellow-40);
--wa-color-warning-min-40: var(--wa-color-min-yellow-40);
--wa-color-warning-max-50: var(--wa-color-max-yellow-50);
--wa-color-warning-min-50: var(--wa-color-min-yellow-50);
--wa-color-warning-max-60: var(--wa-color-max-yellow-60);
--wa-color-warning-min-60: var(--wa-color-min-yellow-60);
--wa-color-warning-max-70: var(--wa-color-max-yellow-70);
--wa-color-warning-min-70: var(--wa-color-min-yellow-70);
--wa-color-danger-05: var(--wa-color-red-05);
--wa-color-danger-10: var(--wa-color-red-10);
--wa-color-danger-20: var(--wa-color-red-20);
--wa-color-danger-30: var(--wa-color-red-30);
--wa-color-danger-40: var(--wa-color-red-40);
--wa-color-danger-50: var(--wa-color-red-50);
--wa-color-danger-60: var(--wa-color-red-60);
--wa-color-danger-70: var(--wa-color-red-70);
--wa-color-danger-80: var(--wa-color-red-80);
--wa-color-danger-90: var(--wa-color-red-90);
--wa-color-danger-95: var(--wa-color-red-95);
--wa-color-danger: var(--wa-color-red);
--wa-color-danger-max-40: var(--wa-color-max-red-40);
--wa-color-danger-min-40: var(--wa-color-min-red-40);
--wa-color-danger-max-50: var(--wa-color-max-red-50);
--wa-color-danger-min-50: var(--wa-color-min-red-50);
--wa-color-danger-max-60: var(--wa-color-max-red-60);
--wa-color-danger-min-60: var(--wa-color-min-red-60);
--wa-color-danger-max-70: var(--wa-color-max-red-70);
--wa-color-danger-min-70: var(--wa-color-min-red-70);
--wa-color-neutral-05: var(--wa-color-gray-05);
--wa-color-neutral-10: var(--wa-color-gray-10);
--wa-color-neutral-20: var(--wa-color-gray-20);
--wa-color-neutral-30: var(--wa-color-gray-30);
--wa-color-neutral-40: var(--wa-color-gray-40);
--wa-color-neutral-50: var(--wa-color-gray-50);
--wa-color-neutral-60: var(--wa-color-gray-60);
--wa-color-neutral-70: var(--wa-color-gray-70);
--wa-color-neutral-80: var(--wa-color-gray-80);
--wa-color-neutral-90: var(--wa-color-gray-90);
--wa-color-neutral-95: var(--wa-color-gray-95);
--wa-color-neutral: var(--wa-color-gray);
--wa-color-neutral-max-40: var(--wa-color-max-gray-40);
--wa-color-neutral-min-40: var(--wa-color-min-gray-40);
--wa-color-neutral-max-50: var(--wa-color-max-gray-50);
--wa-color-neutral-min-50: var(--wa-color-min-gray-50);
--wa-color-neutral-max-60: var(--wa-color-max-gray-60);
--wa-color-neutral-min-60: var(--wa-color-min-gray-60);
--wa-color-neutral-max-70: var(--wa-color-max-gray-70);
--wa-color-neutral-min-70: var(--wa-color-min-gray-70);
}

View File

@@ -0,0 +1,15 @@
:where(:root),
:host,
:where([class^='wa-theme-'], [class*=' wa-theme-']) {
{% for variant, hue in { success: 'green', warning: 'yellow', danger: 'red', neutral: 'gray' } -%}
{%- for tint in tints %}
--wa-color-{{ variant }}-{{ tint }}: var(--wa-color-{{ hue }}-{{ tint }});
{%- endfor %}
--wa-color-{{ variant }}: var(--wa-color-{{ hue }});
{% for tint in ['40', '50', '60', '70'] %}
--wa-color-{{ variant }}-max-{{ tint }}: var(--wa-color-max-{{ hue }}-{{ tint }});
--wa-color-{{ variant }}-min-{{ tint }}: var(--wa-color-min-{{ hue }}-{{ tint }});
{%- endfor %}
{% endfor -%}
}

View File

@@ -19,13 +19,13 @@
*/
--wa-color-brand-fill-quiet: var(--wa-color-brand-95);
--wa-color-brand-fill-normal: var(--wa-color-brand-90);
--wa-color-brand-fill-loud: var(--wa-color-brand-40);
--wa-color-brand-fill-loud: var(--wa-color-brand);
--wa-color-brand-border-quiet: var(--wa-color-brand-90);
--wa-color-brand-border-normal: var(--wa-color-brand-80);
--wa-color-brand-border-loud: var(--wa-color-brand-60);
--wa-color-brand-on-quiet: var(--wa-color-brand-40);
--wa-color-brand-on-normal: var(--wa-color-brand-30);
--wa-color-brand-on-loud: white;
--wa-color-brand-on-loud: var(--wa-color-brand-on);
--wa-color-success-fill-loud: var(--wa-color-green-40);
@@ -51,13 +51,13 @@
*/
--wa-color-brand-fill-quiet: var(--wa-color-brand-10);
--wa-color-brand-fill-normal: var(--wa-color-brand-20);
--wa-color-brand-fill-loud: var(--wa-color-brand-40);
--wa-color-brand-fill-loud: var(--wa-color-brand);
--wa-color-brand-border-quiet: var(--wa-color-brand-20);
--wa-color-brand-border-normal: var(--wa-color-brand-30);
--wa-color-brand-border-loud: var(--wa-color-brand-40);
--wa-color-brand-on-quiet: var(--wa-color-brand-60);
--wa-color-brand-on-normal: var(--wa-color-brand-70);
--wa-color-brand-on-loud: white;
--wa-color-brand-on-loud: var(--wa-color-brand-on);
--wa-color-success-fill-loud: var(--wa-color-green-40);

View File

@@ -29,7 +29,7 @@
*/
--wa-color-brand-fill-quiet: var(--wa-color-brand-90);
--wa-color-brand-fill-normal: var(--wa-color-brand-80);
--wa-color-brand-fill-loud: var(--wa-color-brand-40);
--wa-color-brand-fill-loud: var(--wa-color-brand-max-50);
--wa-color-brand-border-quiet: var(--wa-color-brand-80);
--wa-color-brand-border-normal: var(--wa-color-brand-70);
--wa-color-brand-border-loud: var(--wa-color-brand-60);
@@ -39,7 +39,7 @@
--wa-color-success-fill-quiet: var(--wa-color-green-90);
--wa-color-success-fill-normal: var(--wa-color-green-80);
--wa-color-success-fill-loud: var(--wa-color-green-40);
--wa-color-success-fill-loud: var(--wa-color-green-50);
--wa-color-success-border-quiet: var(--wa-color-green-80);
--wa-color-success-border-normal: var(--wa-color-green-70);
--wa-color-success-border-loud: var(--wa-color-green-60);
@@ -49,7 +49,7 @@
--wa-color-warning-fill-quiet: var(--wa-color-yellow-90);
--wa-color-warning-fill-normal: var(--wa-color-yellow-80);
--wa-color-warning-fill-loud: var(--wa-color-yellow-40);
--wa-color-warning-fill-loud: var(--wa-color-yellow-50);
--wa-color-warning-border-quiet: var(--wa-color-yellow-80);
--wa-color-warning-border-normal: var(--wa-color-yellow-70);
--wa-color-warning-border-loud: var(--wa-color-yellow-60);
@@ -59,7 +59,7 @@
--wa-color-danger-fill-quiet: var(--wa-color-red-90);
--wa-color-danger-fill-normal: var(--wa-color-red-80);
--wa-color-danger-fill-loud: var(--wa-color-red-40);
--wa-color-danger-fill-loud: var(--wa-color-red-50);
--wa-color-danger-border-quiet: var(--wa-color-red-80);
--wa-color-danger-border-normal: var(--wa-color-red-70);
--wa-color-danger-border-loud: var(--wa-color-red-60);
@@ -69,7 +69,7 @@
--wa-color-neutral-fill-quiet: var(--wa-color-gray-90);
--wa-color-neutral-fill-normal: var(--wa-color-gray-80);
--wa-color-neutral-fill-loud: var(--wa-color-gray-40);
--wa-color-neutral-fill-loud: var(--wa-color-gray-50);
--wa-color-neutral-border-quiet: var(--wa-color-gray-80);
--wa-color-neutral-border-normal: var(--wa-color-gray-70);
--wa-color-neutral-border-loud: var(--wa-color-gray-60);

View File

@@ -21,13 +21,13 @@
*/
--wa-color-brand-fill-quiet: var(--wa-color-brand-90);
--wa-color-brand-fill-normal: var(--wa-color-brand-80);
--wa-color-brand-fill-loud: var(--wa-color-brand-50);
--wa-color-brand-fill-loud: var(--wa-color-brand);
--wa-color-brand-border-quiet: var(--wa-color-brand-90);
--wa-color-brand-border-normal: var(--wa-color-brand-70);
--wa-color-brand-border-loud: var(--wa-color-brand-50);
--wa-color-brand-on-quiet: var(--wa-color-brand-30);
--wa-color-brand-on-normal: var(--wa-color-brand-30);
--wa-color-brand-on-loud: white;
--wa-color-brand-on-loud: var(--wa-color-brand-on);
--wa-color-success-fill-quiet: var(--wa-color-green-90);
--wa-color-success-fill-normal: var(--wa-color-green-80);
@@ -94,13 +94,13 @@
*/
--wa-color-brand-fill-quiet: var(--wa-color-brand-20);
--wa-color-brand-fill-normal: var(--wa-color-brand-40);
--wa-color-brand-fill-loud: var(--wa-color-brand-50);
--wa-color-brand-fill-loud: var(--wa-color-brand);
--wa-color-brand-border-quiet: var(--wa-color-brand-30);
--wa-color-brand-border-normal: var(--wa-color-brand-40);
--wa-color-brand-border-loud: var(--wa-color-brand-50);
--wa-color-brand-on-quiet: var(--wa-color-brand-70);
--wa-color-brand-on-normal: var(--wa-color-brand-90);
--wa-color-brand-on-loud: white;
--wa-color-brand-on-loud: var(--wa-color-brand-on);
--wa-color-success-fill-quiet: var(--wa-color-green-20);
--wa-color-success-fill-normal: var(--wa-color-green-40);

View File

@@ -24,13 +24,13 @@
*/
--wa-color-brand-fill-quiet: var(--wa-color-brand-95);
--wa-color-brand-fill-normal: var(--wa-color-brand-90);
--wa-color-brand-fill-loud: var(--wa-color-brand-80);
--wa-color-brand-fill-loud: var(--wa-color-brand);
--wa-color-brand-border-quiet: var(--wa-color-brand-80);
--wa-color-brand-border-normal: var(--wa-color-brand-70);
--wa-color-brand-border-loud: var(--wa-color-brand-50);
--wa-color-brand-on-quiet: var(--wa-color-brand-40);
--wa-color-brand-on-normal: var(--wa-color-brand-30);
--wa-color-brand-on-loud: var(--wa-color-brand-20);
--wa-color-brand-on-loud: var(--wa-color-brand-on);
--wa-color-success-fill-quiet: var(--wa-color-green-95);
--wa-color-success-fill-normal: var(--wa-color-green-90);
@@ -98,13 +98,13 @@
*/
--wa-color-brand-fill-quiet: var(--wa-color-brand-30);
--wa-color-brand-fill-normal: var(--wa-color-brand-40);
--wa-color-brand-fill-loud: var(--wa-color-brand-80);
--wa-color-brand-fill-loud: var(--wa-color-brand);
--wa-color-brand-border-quiet: var(--wa-color-brand-30);
--wa-color-brand-border-normal: var(--wa-color-brand-40);
--wa-color-brand-border-loud: var(--wa-color-brand-70);
--wa-color-brand-on-quiet: var(--wa-color-brand-70);
--wa-color-brand-on-normal: var(--wa-color-brand-90);
--wa-color-brand-on-loud: var(--wa-color-brand-20);
--wa-color-brand-on-loud: var(--wa-color-brand-on);
--wa-color-success-fill-quiet: var(--wa-color-green-30);
--wa-color-success-fill-normal: var(--wa-color-green-40);

View File

@@ -1,7 +1,4 @@
/**
* Do not edit this file directly. It is generated via variants.css.js
*/
/* DO NOT EDIT THIS FILE. It is generated from "variants.css.njk" */
/**
* Register color properties so that the space toggle hack can work.
*/
@@ -64,7 +61,6 @@
* Element defaults.
We want these to resolve to inherit when inside a variant, and only be applied when not inside an explicit variant.
*/
:host(wa-button),
.wa-button,
button,

View File

@@ -1,68 +0,0 @@
/**
* Generate variants.css
* To use: node variants.css.js > variants.css
*/
const variants = ['neutral', 'brand', 'success', 'warning', 'danger'];
const roles = ['fill', 'border', 'on'];
const noise = ['loud', 'normal', 'quiet'];
const defaults = {
neutral: [
[':host(wa-button)', '.wa-button', 'button', "input[type='button']", "input[type='submit']"],
[':host(wa-tag)', '.wa-tag'],
],
brand: [
[':host(wa-callout)', '.wa-callout'],
[':host(wa-badge)', '.wa-badge'],
],
};
const types = roles.map(layer => noise.map(priority => layer + '-' + priority)).flat();
let ret = comment('Do not edit this file directly. It is generated via variants.css.js').trimStart();
ret += comment('Register color properties so that the space toggle hack can work.');
for (let type of types) {
ret += cssRule(`@property --wa-color-${type}`, [
`syntax: '<color>';`,
'inherits: true;',
'initial-value: transparent;',
]);
}
ret += comment(`Element defaults.
We want these to resolve to inherit when inside a variant, and only be applied when not inside an explicit variant.`);
for (let variant in defaults) {
let selector = defaults[variant];
let declarations = types.map(type => `--wa-color-${type}: var(--wa-no-variant, var(--wa-color-${variant}-${type}));`);
ret += cssRule(selector, declarations);
}
ret += comment('Variants');
for (let variant of variants) {
let selector = [`.wa-${variant}`, `:host([variant='${variant}'])`];
if (variant === 'neutral') {
selector.unshift(':root');
}
let declarations = types.map(type => `--wa-color-${type}: var(--wa-color-${variant}-${type});`);
ret += cssRule(selector, declarations);
}
ret += cssRule([variants.map(variant => `.wa-${variant}`), ':host([variant])'], '--wa-no-variant: /* space toggle */;');
console.log(ret.trimEnd());
function cssRule(selector, declarations) {
selector = Array.isArray(selector) ? selector.flat().join(',\n') : selector;
declarations = Array.isArray(declarations) ? declarations.flat().join('\n') : declarations;
declarations = declarations.replace(/^/gm, ' ');
return `\n${selector} {\n${declarations}\n}\n`;
}
function comment(text) {
return ['\n/**', ` * ${text}`, ' */\n'].join('\n');
}

View File

@@ -0,0 +1,48 @@
/**
* Register color properties so that the space toggle hack can work.
*/
{% for type in semantic_color_types %}
@property --wa-color-{{ type }} {
syntax: '<color>';
inherits: true;
initial-value: transparent;
}
{% endfor %}
/**
* Element defaults.
We want these to resolve to inherit when inside a variant, and only be applied when not inside an explicit variant.
*/
:host(wa-button), .wa-button, button, input[type='button'], input[type='submit'],
:host(wa-tag), .wa-tag {
{%- for type in semantic_color_types %}
--wa-color-{{ type }}: var(--wa-no-variant, var(--wa-color-neutral-{{ type }}));
{%- endfor %}
}
:host(wa-callout), .wa-callout,
:host(wa-badge), .wa-badge {
{%- for type in semantic_color_types %}
--wa-color-{{ type }}: var(--wa-no-variant, var(--wa-color-brand-{{ type }}));
{%- endfor %}
}
/**
* Variants
*/
{% for variant in variants %}
{{ ':root,' if variant === 'neutral' }}
.wa-{{ variant }},
:host([variant='{{ variant }}']) {
{%- for type in semantic_color_types %}
--wa-color-{{ type }}: var(--wa-color-{{ variant }}-{{ type }});
{%- endfor %}
}
{%- endfor %}
{% for variant in variants %}
.wa-{{ variant }},
{%- endfor %}
:host([variant]) {
--wa-no-variant: /* space toggle */;
}