mirror of
https://github.com/shoelace-style/webawesome.git
synced 2026-01-13 20:49:15 +00:00
Compare commits
1 Commits
palette-re
...
pill
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2264257093 |
@@ -1,4 +1,3 @@
|
||||
import * as path from 'node:path';
|
||||
import { anchorHeadingsPlugin } from './_utils/anchor-headings.js';
|
||||
import { codeExamplesPlugin } from './_utils/code-examples.js';
|
||||
import { copyCodePlugin } from './_utils/copy-code.js';
|
||||
@@ -9,7 +8,6 @@ import { removeDataAlphaElements } from './_utils/remove-data-alpha-elements.js'
|
||||
// import { formatCodePlugin } from './_utils/format-code.js';
|
||||
import litPlugin from '@lit-labs/eleventy-plugin-lit';
|
||||
import { readFile } from 'fs/promises';
|
||||
import nunjucks from 'nunjucks';
|
||||
import componentList from './_data/componentList.js';
|
||||
import * as filters from './_utils/filters.js';
|
||||
import { outlinePlugin } from './_utils/outline.js';
|
||||
@@ -18,10 +16,7 @@ import { searchPlugin } from './_utils/search.js';
|
||||
|
||||
import process from 'process';
|
||||
|
||||
import * as url from 'url';
|
||||
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
|
||||
|
||||
const packageData = JSON.parse(await readFile(path.join(__dirname, '..', 'package.json'), 'utf-8'));
|
||||
const packageData = JSON.parse(await readFile('./package.json', 'utf-8'));
|
||||
const isAlpha = process.argv.includes('--alpha');
|
||||
const isDev = process.argv.includes('--develop');
|
||||
|
||||
@@ -29,23 +24,12 @@ const globalData = {
|
||||
package: packageData,
|
||||
isAlpha,
|
||||
layout: 'page.njk',
|
||||
|
||||
server: {
|
||||
head: '',
|
||||
loginOrAvatar: '',
|
||||
flashes: '',
|
||||
},
|
||||
};
|
||||
|
||||
const passThroughExtensions = ['js', 'css', 'png', 'svg', 'jpg', 'mp4'];
|
||||
const passThrough = [...passThroughExtensions.map(ext => 'docs/**/*.' + ext)];
|
||||
|
||||
export default function (eleventyConfig) {
|
||||
/**
|
||||
* This is the guard we use for now to make sure our final built files dont need a 2nd pass by the server. This keeps us able to still deploy the bare HTML files on Vercel until the app is ready.
|
||||
*/
|
||||
const serverBuild = process.env.WEBAWESOME_SERVER === 'true';
|
||||
|
||||
// NOTE - alpha setting removes certain pages
|
||||
if (isAlpha) {
|
||||
eleventyConfig.ignores.add('**/experimental/**');
|
||||
@@ -71,38 +55,7 @@ export default function (eleventyConfig) {
|
||||
|
||||
// Shortcodes - {% shortCode arg1, arg2 %}
|
||||
eleventyConfig.addShortcode('cdnUrl', location => {
|
||||
return `https://early.webawesome.com/webawesome@${packageData.version}/dist/` + (location || '').replace(/^\//, '');
|
||||
});
|
||||
|
||||
// Turns `{% server "foo" %} into `{{ server.foo | safe }}` when the WEBAWESOME_SERVER variable is set to "true"
|
||||
eleventyConfig.addShortcode('server', function (property) {
|
||||
if (serverBuild) {
|
||||
return `{{ server.${property} | safe }}`;
|
||||
}
|
||||
|
||||
return '';
|
||||
});
|
||||
|
||||
eleventyConfig.addTransform('second-nunjucks-transform', function NunjucksTransform(content) {
|
||||
// For a server build, we expect a server to run the second transform.
|
||||
if (serverBuild) {
|
||||
return content;
|
||||
}
|
||||
|
||||
// Only run the transform on files nunjucks would transform.
|
||||
if (!this.page.inputPath.match(/.(md|html|njk)$/)) {
|
||||
return content;
|
||||
}
|
||||
|
||||
/** This largely mimics what an app would do and just stubs out what we don't care about. */
|
||||
return nunjucks.renderString(content, {
|
||||
// Stub the server EJS shortcodes.
|
||||
server: {
|
||||
head: '',
|
||||
loginOrAvatar: '',
|
||||
flashes: '',
|
||||
},
|
||||
});
|
||||
return `https://early.webawesome.com/webawesome@${packageData.version}/dist/` + location.replace(/^\//, '');
|
||||
});
|
||||
|
||||
// Paired shortcodes - {% shortCode %}content{% endShortCode %}
|
||||
@@ -164,6 +117,29 @@ export default function (eleventyConfig) {
|
||||
]),
|
||||
);
|
||||
|
||||
// SSR plugin
|
||||
if (!isDev) {
|
||||
//
|
||||
// Problematic components in SSR land:
|
||||
// - animation (breaks on navigation + ssr with Turbo)
|
||||
// - mutation-observer (why SSR this?)
|
||||
// - resize-observer (why SSR this?)
|
||||
// - tooltip (why SSR this?)
|
||||
//
|
||||
const omittedModules = [];
|
||||
const componentModules = componentList
|
||||
.filter(component => !omittedModules.includes(component.tagName.split(/wa-/)[1]))
|
||||
.map(component => {
|
||||
const name = component.tagName.split(/wa-/)[1];
|
||||
return `./dist/components/${name}/${name}.js`;
|
||||
});
|
||||
|
||||
eleventyConfig.addPlugin(litPlugin, {
|
||||
mode: 'worker',
|
||||
componentModules,
|
||||
});
|
||||
}
|
||||
|
||||
// Build the search index
|
||||
eleventyConfig.addPlugin(
|
||||
searchPlugin({
|
||||
@@ -190,31 +166,6 @@ export default function (eleventyConfig) {
|
||||
eleventyConfig.addPassthroughCopy(glob);
|
||||
}
|
||||
|
||||
// SSR plugin
|
||||
// Make sure this is the last thing, we dont want to run the risk of accidentally transforming shadow roots with the nunjucks 2nd transform.
|
||||
if (!isDev) {
|
||||
//
|
||||
// Problematic components in SSR land:
|
||||
// - animation (breaks on navigation + ssr with Turbo)
|
||||
// - mutation-observer (why SSR this?)
|
||||
// - resize-observer (why SSR this?)
|
||||
// - tooltip (why SSR this?)
|
||||
//
|
||||
const omittedModules = [];
|
||||
const componentModules = componentList
|
||||
.filter(component => !omittedModules.includes(component.tagName.split(/wa-/)[1]))
|
||||
.map(component => {
|
||||
const name = component.tagName.split(/wa-/)[1];
|
||||
const componentDirectory = process.env.UNBUNDLED_DIST_DIRECTORY || path.join('.', 'dist');
|
||||
return path.join(componentDirectory, 'components', name, `${name}.js`);
|
||||
});
|
||||
|
||||
eleventyConfig.addPlugin(litPlugin, {
|
||||
mode: 'worker',
|
||||
componentModules,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
markdownTemplateEngine: 'njk',
|
||||
dir: {
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { HUE_RANGES as default } from '../assets/scripts/tweak/data.js';
|
||||
export { hueRanges as default } from '../assets/scripts/tweak/data.js';
|
||||
|
||||
@@ -50,9 +50,6 @@
|
||||
Search
|
||||
<kbd slot="suffix" class="only-desktop">/</kbd>
|
||||
</wa-button>
|
||||
|
||||
{# Login #}
|
||||
{% server "loginOrAvatar" %}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -79,19 +76,14 @@
|
||||
</aside>
|
||||
{% endif %}
|
||||
|
||||
|
||||
{# Main #}
|
||||
<main id="content">
|
||||
{# Expandable outline #}
|
||||
{% if hasOutline %}
|
||||
<nav id="outline-expandable">
|
||||
<details class="outline-links">
|
||||
<summary>On this page</summary>
|
||||
</details>
|
||||
</nav>
|
||||
{% endif %}
|
||||
|
||||
<div id="flashes">{% server "flashes" %}</div>
|
||||
|
||||
{% block header %}
|
||||
{% include 'breadcrumbs.njk' %}
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
{% set ancestors = page.url | ancestors %}
|
||||
|
||||
{% if ancestors.length > 0 %}
|
||||
{% set breadcrumbs = page.url | breadcrumbs %}
|
||||
{% if breadcrumbs.length > 0 %}
|
||||
<wa-breadcrumb id="docs-breadcrumbs">
|
||||
{% for ancestor in ancestors %}
|
||||
{% if ancestor.page.url != "/" %}
|
||||
<wa-breadcrumb-item href="{{ ancestor.page.url }}">{{ ancestor.data.title }}</wa-breadcrumb-item>
|
||||
{% endif %}
|
||||
{% for crumb in breadcrumbs %}
|
||||
<wa-breadcrumb-item href="{{ crumb.url }}">{{ crumb.title }}</wa-breadcrumb-item>
|
||||
{% endfor %}
|
||||
<wa-breadcrumb-item>{# Current page #}</wa-breadcrumb-item>
|
||||
</wa-breadcrumb>
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
{# Cards for pages listed by category #}
|
||||
|
||||
<section id="grid" class="index-grid">
|
||||
{% set groupedPages = allPages | groupPages(categories, page) %}
|
||||
{% for category, pages in groupedPages -%}
|
||||
{% if groupedPages.meta.groupCount > 1 %}
|
||||
<h2 class="index-category">
|
||||
{% if pages.meta.url %}<a href="{{ pages.meta.url }}">{{ pages.meta.title }}</a>
|
||||
{% else %}
|
||||
{{ pages.meta.title }}
|
||||
{% endif %}
|
||||
</h2>
|
||||
{% endif %}
|
||||
{%- for page in pages -%}
|
||||
{% include "page-card.njk" %}
|
||||
{%- endfor -%}
|
||||
{% for category, pages in allPages | groupByTags(categories) -%}
|
||||
<h2 class="index-category">{{ category | getCategoryTitle(categories) }}</h2>
|
||||
{%- for page in pages -%}
|
||||
{%- if not page.data.parent or listChildren -%}
|
||||
{% include "page-card.njk" %}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- endfor -%}
|
||||
</section>
|
||||
|
||||
@@ -47,7 +47,3 @@
|
||||
<link rel="stylesheet" href="/dist/styles/webawesome.css" />
|
||||
<link id="color-stylesheet" rel="stylesheet" href="/dist/styles/utilities.css" />
|
||||
<link rel="stylesheet" href="/dist/styles/forms.css" />
|
||||
|
||||
|
||||
{# Used by Web Awesome App to inject other assets into the head. #}
|
||||
{% server "head" %}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<a href="{{ page.url }}"{{ page.data.keywords | attr('data-keywords') }}>
|
||||
<wa-card with-header>
|
||||
<div slot="header">
|
||||
{% include "svgs/" + (page.data.icon or "thumbnail-placeholder") + ".njk" ignore missing %}
|
||||
{% include "svgs/" + (page.data.icon or "thumbnail-placeholder") + ".njk" %}
|
||||
</div>
|
||||
<span class="page-name">{{ page.data.title }}</span>
|
||||
{% if pageSubtitle -%}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
<table class="colors main wa-palette-{{ paletteId }} static-palette">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th class="core-column">Core tint</th>
|
||||
{% for tint in tints -%}
|
||||
<th>{{ tint }}</th>
|
||||
{%- endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{%- set hueBefore = hues[hues|length - 2] -%}
|
||||
{% for hue in hues -%}
|
||||
{% set scale = palettes[paletteId][hue] %}
|
||||
{% set coreTint = scale.maxChromaTint %}
|
||||
{%- set coreColor = scale[coreTint] -%}
|
||||
{%- set maxChroma = coreColor.c if coreColor.c > maxChroma else maxChroma -%}
|
||||
<tr data-hue="{{ hue }}" class="color-scale" style="--swatch-text-color: light-dark(var(--wa-color-{{ hue }}-10), white)">
|
||||
<th>{{ hue | capitalize }}</th>
|
||||
<td class="core-column" style="--color: var(--wa-color-{{ hue }})">
|
||||
<div class="color swatch" style="color-scheme: {{ 'light' if scale.maxChromaTint > 60 else 'dark' }};">
|
||||
{{ scale.maxChromaTint }}
|
||||
</div>
|
||||
</td>
|
||||
{% for tint in tints -%}
|
||||
{%- set color = scale[tint] -%}
|
||||
<td style="--color: var(--wa-color-{{ hue }}-{{ tint }}); color-scheme: ">
|
||||
<div class="color swatch" style="color-scheme: {{ 'light' if tint > 60 else 'dark' }};">
|
||||
<wa-copy-button value="--wa-color-{{ hue }}-{{ tint }}" copy-label="--wa-color-{{ hue }}-{{ tint }}"></wa-copy-button>
|
||||
</div>
|
||||
</td>
|
||||
{%- endfor -%}
|
||||
</tr>
|
||||
</tbody>
|
||||
{% endfor %}
|
||||
</table>
|
||||
@@ -1,12 +1,9 @@
|
||||
{# Some collections (like "patterns") will not have any items in the alpha build for example. So this checks to make sure the collection exists. #}
|
||||
{% if collections[tag] -%}
|
||||
{% set groupUrl %}/docs/{{ tag }}/{% endset %}
|
||||
{% set groupItem = groupUrl | getCollectionItemFromUrl %}
|
||||
{% set children = groupItem.data.children if groupItem.data.children.length > 0 else (collections[tag] | sort) %}
|
||||
|
||||
<wa-details {{ ((tag in (tags or [])) or (groupUrl in page.url)) | attr('open') }}>
|
||||
<h2 slot="summary">
|
||||
{% if groupItem %}
|
||||
{% if groupUrl | getCollectionItemFromUrl %}
|
||||
<a href="{{ groupUrl }}" title="Overview">{{ title or (tag | capitalize) }}
|
||||
<wa-icon name="grid-2"></wa-icon>
|
||||
</a>
|
||||
@@ -15,8 +12,10 @@
|
||||
{% endif %}
|
||||
</h2>
|
||||
<ul>
|
||||
{% for page in children %}
|
||||
{% for page in collections[tag] | sort %}
|
||||
{% if not page.data.parent -%}
|
||||
{% include 'sidebar-link.njk' %}
|
||||
{%- endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</wa-details>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% if not (isAlpha and page.data.noAlpha) and not page.data.unlisted -%}
|
||||
{% if not (isAlpha and page.data.noAlpha) and page.fileSlug != tag and not page.data.unlisted -%}
|
||||
<li>
|
||||
<a href="{{ page.url }}">{{ page.data.title }}</a>
|
||||
{% if page.data.status == 'experimental' %}<wa-icon name="flask"></wa-icon>{% endif %}
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
{% if since -%}
|
||||
<wa-badge variant="neutral" class="since">Since {{ since }}</wa-badge>
|
||||
<wa-badge variant="neutral">Since {{ since }}</wa-badge>
|
||||
{% endif -%}
|
||||
|
||||
{%- if status %}
|
||||
{%- if status == "wip" %}
|
||||
<wa-badge variant="danger" class="status">
|
||||
<wa-badge variant="danger">
|
||||
<wa-icon name="pickaxe"></wa-icon>
|
||||
Work In Progress
|
||||
</wa-badge>
|
||||
{%- elif status == "experimental" %}
|
||||
<wa-badge variant="warning" class="status">
|
||||
<wa-badge variant="warning">
|
||||
<wa-icon name="flask"></wa-icon>
|
||||
Experimental
|
||||
</wa-badge>
|
||||
{%- elif status == "stable" %}
|
||||
<wa-badge variant="brand" class="status">Stable</wa-badge>
|
||||
<wa-badge variant="brand">Stable</wa-badge>
|
||||
{%- else %}
|
||||
<wa-badge class="status">{{ status}}</wa-badge>
|
||||
<wa-badge>{{ status}}</wa-badge>
|
||||
{%- endif -%}
|
||||
{%- endif %}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
layout: page-outline
|
||||
tags: ["overview"]
|
||||
---
|
||||
{% set forTag = forTag or (page.url | split('/') | last) %}
|
||||
{% if description %}
|
||||
@@ -12,10 +13,8 @@ layout: page-outline
|
||||
</wa-input>
|
||||
</div>
|
||||
|
||||
{% set allPages = allPages or collections[forTag] %}
|
||||
{% if allPages and allPages.length > 0 %}
|
||||
{% set allPages = collections[forTag] %}
|
||||
{% include "grouped-pages.njk" %}
|
||||
{% endif %}
|
||||
|
||||
<link href="/assets/styles/filter.css" rel="stylesheet">
|
||||
<script type="module" src="/assets/scripts/filter.js"></script>
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
{% if hasSidebar == undefined %}
|
||||
{% set hasSidebar = true %}
|
||||
{% endif %}
|
||||
|
||||
{% if hasOutline == undefined %}
|
||||
{% set hasOutline = false %}
|
||||
{% endif %}
|
||||
{% set hasSidebar = true %}
|
||||
{% set hasOutline = false %}
|
||||
|
||||
{% extends "../_includes/base.njk" %}
|
||||
|
||||
@@ -7,46 +7,38 @@
|
||||
|
||||
{% block head %}
|
||||
<style>@import url('/dist/styles/color/{{ paletteId }}.css') layer(palette.{{ paletteId }});</style>
|
||||
<link href="{{ page.url }}../app/tweak.css" rel="stylesheet">
|
||||
<script type="module" src="{{ page.url }}../app/tweak.js"></script>
|
||||
<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-slug="{{ page.fileSlug }}" data-palette-id="{{ page.fileSlug }}">
|
||||
<div id="palette-app" data-palette-id="{{ paletteId }}">
|
||||
<div
|
||||
:class="{
|
||||
tweaking: tweaking.chroma,
|
||||
'tweaking-chroma': tweaking.chroma,
|
||||
'tweaking-hue': tweaking.chroma,
|
||||
'tweaking-gray-chroma': tweaking.grayChroma,
|
||||
'tweaked-chroma': tweaked?.chroma,
|
||||
'tweaked-hue': tweaked?.hue,
|
||||
'tweaked-any': Object.keys(tweaksHumanReadable).length,
|
||||
'tweaked-any': tweaked
|
||||
}"
|
||||
:style="{
|
||||
'--chroma-scale': chromaScale,
|
||||
'--gray-chroma': tweaked?.grayChroma ? grayChroma : originalGrayChroma,
|
||||
'--max-c': maxChroma,
|
||||
'--avg-l': L_RANGES[level].mid,
|
||||
'--gray-chroma': tweaked?.grayChroma ? grayChroma : '',
|
||||
}">
|
||||
|
||||
<header id="palette-info">
|
||||
{% include 'breadcrumbs.njk' %}
|
||||
|
||||
<h1 class="title">
|
||||
<span v-content="saved?.title || (step > 0 ? defaultPaletteTitle : paletteTitle)">{{ title }}</span>
|
||||
<template v-if="saved || step > 0">
|
||||
<wa-icon-button name="pencil" label="Rename palette" @click="rename"></wa-icon-button>
|
||||
<wa-icon-button v-if="saved" class="delete" name="trash" label="Delete palette" @click="deleteSaved"></wa-icon-button>
|
||||
<wa-button @click="save()" :disabled="!unsavedChanges"
|
||||
:variant="unsavedChanges ? 'success' : 'neutral'" size="small" :appearance="unsavedChanges ? 'accent' : 'outlined'">
|
||||
<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>
|
||||
<span v-content="unsavedChanges ? 'Save' : 'Saved'">Save</span>
|
||||
</wa-button>
|
||||
</template>
|
||||
<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" v-cloak>
|
||||
<code class="class">.wa-palette-<span v-content="slug">{{ page.fileSlug }}</span></code>
|
||||
<div class="block-info">
|
||||
<code class="class">.wa-palette-{{ paletteId }}</code>
|
||||
{% include '../_includes/status.njk' %}
|
||||
{% if not isPro %}
|
||||
<wa-badge class="pro" v-if="tweaked">PRO</wa-badge>
|
||||
@@ -57,28 +49,17 @@
|
||||
{{ description | inlineMarkdown | safe }}
|
||||
</p>
|
||||
{% endif %}
|
||||
<div class="hue-wheel">
|
||||
<template v-for="color, hue in coreColors">
|
||||
<div :id="`hue-wheel-${hue}`" class="color"
|
||||
:style="{
|
||||
'--color': color,
|
||||
'--h': color.get('oklch.h'),
|
||||
'--c': color.get('oklch.c'),
|
||||
'--l': color.get('oklch.l'),
|
||||
}"></div>
|
||||
<wa-tooltip :for="`hue-wheel-${ hue }`" hoist v-content="capitalize(hue) + ' ' + coreLevels[hue]"></wa-tooltip>
|
||||
</template>
|
||||
</div>
|
||||
</header>
|
||||
{% endblock %}
|
||||
|
||||
{% block afterContent %}
|
||||
|
||||
{% set maxChroma = 0 %}
|
||||
|
||||
<wa-callout size="small" class="tweaked-callout" variant="warning">
|
||||
<wa-icon name="sliders-simple" slot="icon" variant="regular"></wa-icon>
|
||||
This palette has been tweaked.
|
||||
<div class="wa-cluster wa-gap-xs">
|
||||
<wa-tag v-for="tweakHumanReadable, param in tweaksHumanReadable" removable @wa-remove="reset(param)" v-content="tweakHumanReadable"></wa-tag>
|
||||
<wa-tag v-for="tweakHumanReadable, param in tweaksHumanReadable" removable @wa-remove="reset(param)">{% raw %}{{ tweakHumanReadable }}{% endraw %}</wa-tag>
|
||||
</div>
|
||||
|
||||
<wa-button @click="reset()" appearance="outlined" variant="danger">
|
||||
@@ -87,12 +68,15 @@
|
||||
</span>
|
||||
Reset
|
||||
</wa-button>
|
||||
<wa-button v-if="!saved" @click="save" variant="success">
|
||||
<span slot="prefix" class="icon-modifier">
|
||||
<wa-icon name="sidebar" variant="regular"></wa-icon>
|
||||
<wa-icon name="circle-plus" class="modifier" style="color: light-dark(var(--wa-color-green-70), var(--wa-color-green-60));"></wa-icon>
|
||||
</span>
|
||||
Save
|
||||
</wa-button>
|
||||
</wa-callout>
|
||||
|
||||
<h2>Scales</h2>
|
||||
|
||||
{% include "palette.njk" %}
|
||||
|
||||
<table class="colors main wa-palette-{{ paletteId }}">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -103,68 +87,127 @@
|
||||
{%- endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody v-cloak>
|
||||
<tr v-for="hue of allHues" :data-hue="hue" :key="hue"
|
||||
class="color-scale" :class="{
|
||||
tweaked: hue === 'gray' ? tweaked.grayChroma || tweaked.grayColor : hueShifts[hue],
|
||||
}"
|
||||
:style="{
|
||||
'--swatch-text-color': `light-dark(var(--wa-color-${ hue }-10), white)`,
|
||||
'--hue-shift': hueShifts[hue] || ''
|
||||
}">
|
||||
<th v-content="capitalize(hue)"></th>
|
||||
<td class="core-column" :style="{'--original-color': `var(--wa-color-${ hue })`, '--color': colors[hue][coreLevels[hue]]}">
|
||||
<color-popup :title="capitalize(hue) + ' (core)'" :token="`--wa-color-${ hue }`" :color="coreColors[hue]">
|
||||
<div slot="trigger" :id="`core-${ hue }-swatch`" class="color swatch" :style="{colorScheme: coreLevels[hue] > 60 ? 'light' : 'dark'}">
|
||||
<span v-content="coreLevels[hue]"></span>
|
||||
<wa-icon name="sliders-simple" class="tweak-icon"></wa-icon>
|
||||
{# Initialize to last hue before gray #}
|
||||
{%- set hueBefore = hues[hues|length - 2] -%}
|
||||
{% for hue in hues -%}
|
||||
{% set coreTint = palettes[paletteId][hue].maxChromaTint %}
|
||||
{%- set coreColor = palettes[paletteId][hue][coreTint] -%}
|
||||
{%- set maxChroma = coreColor.c if coreColor.c > maxChroma else maxChroma -%}
|
||||
{% if hue === 'gray' %}
|
||||
<tr data-hue="{{ hue }}" class="color-scale"
|
||||
:class="{tweaking: tweaking.grayChroma, tweaked: tweaked.grayChroma || tweaked.grayColor }">
|
||||
{% else %}
|
||||
<tr data-hue="{{ hue }}" class="color-scale"
|
||||
:class="{tweaking: tweaking.{{ hue }}, tweaked: hueShifts.{{ hue }} }"
|
||||
:style="{ '--hue-shift': hueShifts.{{ hue }} || '' }">
|
||||
{% endif %}
|
||||
<th>
|
||||
{{ hue | capitalize }}
|
||||
</th>
|
||||
<td class="core-column"
|
||||
style="--color: var(--wa-color-{{ hue }})"
|
||||
:style="{
|
||||
'--color-tweaked': colors.{{ hue }}[{{ coreTint }}],
|
||||
'--color-gray-undertone': colors[grayColor][{{coreTint}}],
|
||||
'--color-tweaked-no-gray-chroma': colorsMinusGrayChroma.{{ hue }}[{{ coreTint }}],
|
||||
}">
|
||||
<wa-dropdown>
|
||||
<div slot="trigger" id="core-{{ hue }}-swatch" data-tint="core" class="color swatch"
|
||||
style="background-color: var(--wa-color-{{ hue }}); color: var(--wa-color-{{ hue }}-{{ '05' if palettes[paletteId][hue].maxChromaTint > 60 else '95' }});"
|
||||
>
|
||||
{{ palettes[paletteId][hue].maxChromaTint }}
|
||||
<wa-icon name="sliders-simple" class="tweak-icon"></wa-icon>
|
||||
</div>
|
||||
<div class="popup">
|
||||
{% if hue === 'gray' %}
|
||||
<wa-radio-group class="core-color" orientation="horizontal" v-model="grayColor">
|
||||
{% for h in hues -%}
|
||||
{%- if h !== 'gray' -%}
|
||||
<wa-radio-button id="gray-undertone-{{ h }}" value="{{ h }}" label="{{ h | capitalize }}" style="--color: var(--wa-color-{{ h }})"></wa-radio-button>
|
||||
<wa-tooltip for="gray-undertone-{{ h }}" hoist>
|
||||
{{ h | capitalize }}
|
||||
</wa-tooltip>
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
<div slot="label">
|
||||
Gray undertone
|
||||
</div>
|
||||
<template #content>
|
||||
<template v-if="hue === 'gray'">
|
||||
<color-swatch-picker :model-value="computedGrayColor" @update:model-value="grayColor = $event" label="Gray undertone" :colors="coreColors"></color-swatch-picker>
|
||||
</template>
|
||||
<template v-else>
|
||||
<color-slider v-if="baseCoreColors[hue]"
|
||||
coord="h" type="shift"
|
||||
v-model="hueShifts[hue]"
|
||||
:default-color="baseCoreColors[hue]"
|
||||
:min="HUE_RANGES[hue].min + 1" :max="HUE_RANGES[hue].max"
|
||||
label="Adjust hue" :label-min="moreHue[hueBefore[hue]]" :label-max="moreHue[hueAfter[hue]]"
|
||||
></color-slider>
|
||||
</template>
|
||||
</wa-radio-group>
|
||||
<div class="decorated-slider gray-chroma-slider" :style="{'--max': maxGrayChroma}">
|
||||
<wa-slider name="gray-chroma" v-model="grayChroma" ref="grayChromaSlider"
|
||||
value="0" min="0" :max="maxGrayChroma" step="0.01"
|
||||
@input="tweaking.grayChroma = true" @change="tweaking.grayChroma = false">
|
||||
<div slot="label">
|
||||
Gray colorfulness
|
||||
<wa-icon-button @click="grayChroma = originalGrayChroma" class="clear-button" name="circle-xmark" library="system" variant="regular" label="Reset"></wa-icon-button>
|
||||
</div>
|
||||
</wa-slider>
|
||||
<div class="label-min">Neutral</div>
|
||||
<div class="label-max" v-content="moreHue[grayColor]">Warmer/Cooler</div>
|
||||
</div>
|
||||
{% else %}
|
||||
{%- set hueAfter = hues[loop.index0 + 1] -%}
|
||||
{%- set hueAfter = hues[0] if hueAfter == 'gray' else hueAfter -%}
|
||||
{%- set minShift = hueRanges[hue].min - coreColor.h | round -%}
|
||||
{%- set maxShift = hueRanges[hue].max - coreColor.h | round -%}
|
||||
|
||||
<color-slider v-if="hue === 'gray'" coord="c" type="scale"
|
||||
:model-value="computedGrayChroma"
|
||||
@update:model-value="grayChroma = $event"
|
||||
:default-color="baseCoreColors[computedGrayColor]"
|
||||
:base-value="baseCoreColors[originalGrayColor].oklch.c"
|
||||
:default-value-relative="originalGrayChroma"
|
||||
:min="0" :max-relative="maxGrayChroma" :step="0.00001"
|
||||
label="Gray colorfulness" label-min="Neutral" :label-max="moreHue[computedGrayColor]"
|
||||
></color-slider>
|
||||
</template>
|
||||
</color-popup>
|
||||
</td>
|
||||
<td v-for="tint in tints.toReversed()" :data-tint="tint" :style="{'--original-color': `var(--wa-color-${ hue }-${tint})`, '--color': colors[hue][tint] }">
|
||||
<color-popup :title="capitalize(hue) + ' ' + tint" :token="`--wa-color-${ hue }-${ tint }`" :color="colors[hue][tint]">
|
||||
<div slot="trigger" class="color swatch" :style="{ colorScheme: tint > 60 ? 'light' : 'dark' }">
|
||||
<wa-icon name="copy" variant="regular" class="copy-icon"></wa-icon>
|
||||
<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>
|
||||
</color-popup>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</wa-slider>
|
||||
<div class="label-min">More {{hueBefore}}</div>
|
||||
<div class="label-max">More {{hueAfter}}</div>
|
||||
</div>
|
||||
{%- set hueBefore = hue -%}
|
||||
{% endif %}
|
||||
<div class="wa-gap-s">
|
||||
<code>--wa-color-{{ hue }}</code>
|
||||
<wa-copy-button value="--wa-color-{{ hue }}" copy-label="--wa-color-{{ hue }}"></wa-copy-button>
|
||||
</div>
|
||||
</div>`
|
||||
</wa-dropdown>
|
||||
</td>
|
||||
{% for tint in tints -%}
|
||||
{%- set color = palettes[paletteId][hue][tint] -%}
|
||||
<td data-tint="{{ tint }}" style="--color: var(--wa-color-{{ hue }}-{{ tint }})"
|
||||
:style="{
|
||||
'--color-tweaked': colors.{{ hue }}[{{ tint }}],
|
||||
'--color-tweaked-no-gray-chroma': colorsMinusGrayChroma.{{ hue }}[{{ tint }}],
|
||||
}">
|
||||
<div class="color swatch" style="--color: var(--wa-color-{{ hue }}-{{ tint }})">
|
||||
<wa-copy-button value="--wa-color-{{ hue }}-{{ tint }}" copy-label="--wa-color-{{ hue }}-{{ tint }}"></wa-copy-button>
|
||||
</div>
|
||||
</td>
|
||||
{%- endfor -%}
|
||||
</tr>
|
||||
{%- endfor %}
|
||||
</table>
|
||||
|
||||
<color-slider :class="{ tweaked: chromaScale !== 1 }"
|
||||
type="scale"
|
||||
v-model="chromaScale"
|
||||
coord="c"
|
||||
:default-color="baseMaxChromaColor"
|
||||
:default-value="baseMaxChroma"
|
||||
:min="MAX_CHROMA_BOUNDS.min" :max="MAX_CHROMA_BOUNDS.max" :step="0.001"
|
||||
label="Overall colorfulness" label-min="More muted" label-max="More vibrant"
|
||||
></color-slider>
|
||||
{% set chromaScaleBounds = [
|
||||
(0.08 / maxChroma) | number({maximumFractionDigits: 2}),
|
||||
(0.3 / maxChroma]) | number({maximumFractionDigits: 2}) -%}
|
||||
<div class="decorated-slider chroma-scale-slider wa-palette-{{ paletteId }}"
|
||||
:class="{ tweaked: chromaScale !== 1 }"
|
||||
style="--min: {{ chromaScaleBounds[0] }}; --max: {{ chromaScaleBounds[1] }};">
|
||||
<wa-slider name="chroma-scale" ref="chromaScaleSlider"
|
||||
v-model="chromaScale" value="1" step="0.01"
|
||||
min="{{ chromaScaleBounds[0] }}" max="{{ chromaScaleBounds[1] }}"
|
||||
@input="tweaking.chroma = true"
|
||||
@change="tweaking.chroma = false">
|
||||
<div slot="label">
|
||||
Overall colorfulness
|
||||
<wa-icon-button @click="chromaScale = 1" class="clear-button" name="circle-xmark" library="system" variant="regular" label="Reset"></wa-icon-button>
|
||||
</div>
|
||||
</wa-slider>
|
||||
<div class="label-min">More muted</div>
|
||||
<div class="label-max">More vibrant</div>
|
||||
</div>
|
||||
|
||||
<h2>Used By</h2>
|
||||
|
||||
@@ -267,22 +310,8 @@ Add the following code at the top of your CSS file:
|
||||
</wa-tab-panel>
|
||||
</wa-tab-group>
|
||||
|
||||
|
||||
{% endmarkdown %}
|
||||
|
||||
<section id="saved" class="index-grid" v-if="savedVariations?.length">
|
||||
<h2 class="index-category">Saved variations</h2>
|
||||
<a v-for="palette of savedVariations" :href="'/docs/palettes/' + palette.id">
|
||||
<wa-card with-header>
|
||||
<div slot="header">
|
||||
{# {% include "svgs/palette.njk" %} #}
|
||||
{% include "svgs/thumbnail-placeholder.njk" %}
|
||||
</div>
|
||||
<span class="page-name" v-text="palette.title"></span>
|
||||
</wa-card>
|
||||
</a>
|
||||
</section>
|
||||
|
||||
|
||||
</div></div> {# end palette app #}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ wa_data.palettes = {
|
||||
<wa-option label="{{ palette.data.title }}" value="{{ palette.fileSlug if not currentPalette }}" {{ (palette.fileSlug if currentPalette) | attr('data-id') }}>
|
||||
<wa-card with-header>
|
||||
<div slot="header">
|
||||
{% include "svgs/" + (palette.data.icon or "thumbnail-placeholder") + ".njk" ignore missing %}
|
||||
{% include "svgs/" + (palette.data.icon or "thumbnail-placeholder") + ".njk" %}
|
||||
</div>
|
||||
<span class="page-name">
|
||||
{{ palette.data.title }}
|
||||
@@ -81,7 +81,7 @@ wa_data.palettes = {
|
||||
{% set palette = defaultPalette %}
|
||||
</wa-select>
|
||||
|
||||
<wa-select class="color-select" name="brand" label="Brand color" value="" clearable>
|
||||
<wa-select name="brand" label="Brand color" value="" clearable>
|
||||
<div class="selected-swatch" slot="prefix"></div>
|
||||
{% for hue in hues %}
|
||||
{% set currentBrand = hue == brand %}
|
||||
|
||||
@@ -29,9 +29,6 @@ function getCollection(name) {
|
||||
}
|
||||
|
||||
export function getCollectionItemFromUrl(url, collection) {
|
||||
if (!url) {
|
||||
return null;
|
||||
}
|
||||
collection ??= getCollection.call(this, 'all') || [];
|
||||
return collection.find(item => item.url === url);
|
||||
}
|
||||
@@ -45,33 +42,35 @@ export function split(text, separator) {
|
||||
return (text + '').split(separator).filter(Boolean);
|
||||
}
|
||||
|
||||
export function ancestors(url, { withCurrent = false, withRoot = false } = {}) {
|
||||
let ret = [];
|
||||
let currentUrl = url;
|
||||
let currentItem = getCollectionItemFromUrl.call(this, url);
|
||||
export function breadcrumbs(url, { withCurrent = false } = {}) {
|
||||
const parts = split(url, '/');
|
||||
const ret = [];
|
||||
|
||||
if (!currentItem) {
|
||||
// Might have eleventyExcludeFromCollections, jump to parent
|
||||
let parentUrl = this.ctx.parentUrl;
|
||||
if (parentUrl) {
|
||||
url = parentUrl;
|
||||
while (parts.length) {
|
||||
let partialUrl = '/' + parts.join('/') + '/';
|
||||
let item = getCollectionItemFromUrl.call(this, partialUrl);
|
||||
|
||||
if (item && (partialUrl !== url || withCurrent)) {
|
||||
let title = item.data.title;
|
||||
if (title) {
|
||||
ret.unshift({ url: partialUrl, title });
|
||||
}
|
||||
}
|
||||
|
||||
parts.pop();
|
||||
|
||||
if (item?.data.parent) {
|
||||
let parentURL = item.data.parent;
|
||||
if (!item.data.parent.startsWith('/')) {
|
||||
// Parent is in the same directory
|
||||
parts.push(item.data.parent);
|
||||
parentURL = '/' + parts.join('/') + '/';
|
||||
}
|
||||
|
||||
let parentBreadcrumbs = breadcrumbs.call(this, parentURL, { withCurrent: true });
|
||||
return [...parentBreadcrumbs, ...ret];
|
||||
}
|
||||
}
|
||||
|
||||
for (let item; (item = getCollectionItemFromUrl.call(this, url)); url = item.data.parentUrl) {
|
||||
ret.unshift(item);
|
||||
}
|
||||
|
||||
if (!withRoot && ret[0]?.page.url === '/') {
|
||||
// Remove root
|
||||
ret.shift();
|
||||
}
|
||||
|
||||
if (!withCurrent && ret.at(-1)?.page.url === currentUrl) {
|
||||
// Remove current page
|
||||
ret.pop();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -181,178 +180,69 @@ export function sort(arr, by = { 'data.order': 1, 'data.title': '' }) {
|
||||
/**
|
||||
* Group an 11ty collection (or any array of objects with a `data.tags` property) by certain tags.
|
||||
* @param {object[]} collection
|
||||
* @param { Object<string, string> | string[]} [options] Options object or array of tags to group by.
|
||||
* @param {string[] | true} [options.tags] Tags to group by. If true, groups by all tags.
|
||||
* If not provided/empty, defaults to grouping by page hierarchy, with any pages with more than 1 children becoming groups.
|
||||
* @param {string[]} [options.groups] The groups to use if only a subset or a specific order is desired. Defaults to `options.tags`.
|
||||
* @param {string[]} [options.titles] Any title overrides for groups.
|
||||
* @param {string | false} [options.other="Other"] The title to use for the "Other" group. If `false`, the "Other" group is removed..
|
||||
* @returns { Object.<string, object[]> } An object of group ids to arrays of page objects.
|
||||
* @param { Object<string, string> | (string | Object<string, string>)[]} [tags] The tags to group by. If not provided/empty, defaults to grouping by all tags.
|
||||
* @returns { Object.<string, object[]> } An object with keys for each tag, and an array of items for each tag.
|
||||
*/
|
||||
export function groupPages(collection, options = {}, page) {
|
||||
export function groupByTags(collection, tags) {
|
||||
if (!collection) {
|
||||
console.error(`Empty collection passed to groupPages() to group by ${JSON.stringify(options)}`);
|
||||
console.error(`Empty collection passed to groupByTags() to group by ${JSON.stringify(tags)}`);
|
||||
}
|
||||
if (!tags) {
|
||||
// Default to grouping by union of all tags
|
||||
tags = Array.from(new Set(collection.flatMap(item => item.data.tags)));
|
||||
} else if (Array.isArray(tags)) {
|
||||
// May contain objects of one-off tag -> label mappings
|
||||
tags = tags.map(tag => (typeof tag === 'object' ? Object.keys(tag)[0] : tag));
|
||||
} else if (typeof tags === 'object') {
|
||||
// tags is an object of tags to labels, so we just want the keys
|
||||
tags = Object.keys(tags);
|
||||
}
|
||||
|
||||
if (Array.isArray(options)) {
|
||||
options = { tags: options };
|
||||
}
|
||||
|
||||
let { tags, groups, titles = {}, other = 'Other' } = options;
|
||||
|
||||
if (groups === undefined && Array.isArray(tags)) {
|
||||
groups = tags;
|
||||
}
|
||||
|
||||
let grouping;
|
||||
|
||||
if (tags) {
|
||||
grouping = {
|
||||
isGroup: item => undefined,
|
||||
getCandidateGroups: item => item.data.tags,
|
||||
getGroupMeta: group => ({}),
|
||||
};
|
||||
} else {
|
||||
grouping = {
|
||||
isGroup: item => (item.data.children.length >= 2 ? item.page.url : undefined),
|
||||
getCandidateGroups: item => {
|
||||
let parentUrl = item.data.parentUrl;
|
||||
if (page?.url === parentUrl) {
|
||||
return [];
|
||||
}
|
||||
return [parentUrl];
|
||||
},
|
||||
getGroupMeta: group => {
|
||||
let item = byUrl[group] || getCollectionItemFromUrl.call(this, group);
|
||||
return {
|
||||
title: item?.data.title,
|
||||
url: group,
|
||||
item,
|
||||
};
|
||||
},
|
||||
sortGroups: groups => sort(groups.map(url => byUrl[url]).filter(Boolean)).map(item => item.page.url),
|
||||
};
|
||||
}
|
||||
|
||||
let byUrl = {};
|
||||
let byParentUrl = {};
|
||||
let ret = Object.fromEntries(tags.map(tag => [tag, []]));
|
||||
ret.other = [];
|
||||
|
||||
for (let item of collection) {
|
||||
let url = item.page.url;
|
||||
let parentUrl = item.data.parentUrl;
|
||||
let categorized = false;
|
||||
|
||||
byUrl[url] = item;
|
||||
|
||||
if (parentUrl) {
|
||||
byParentUrl[parentUrl] ??= [];
|
||||
byParentUrl[parentUrl].push(item);
|
||||
}
|
||||
}
|
||||
|
||||
let urlToGroups = {};
|
||||
|
||||
for (let item of collection) {
|
||||
let url = item.page.url;
|
||||
let parentUrl = item.data.parentUrl;
|
||||
|
||||
if (grouping.isGroup(item)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let parentItem = byUrl[parentUrl];
|
||||
if (parentItem && !grouping.isGroup(parentItem)) {
|
||||
// Their parent is also here and is not a group
|
||||
continue;
|
||||
}
|
||||
|
||||
let candidateGroups = grouping.getCandidateGroups(item);
|
||||
|
||||
if (groups) {
|
||||
candidateGroups = candidateGroups.filter(group => groups.includes(group));
|
||||
}
|
||||
|
||||
urlToGroups[url] ??= [];
|
||||
|
||||
for (let group of candidateGroups) {
|
||||
urlToGroups[url].push(group);
|
||||
}
|
||||
}
|
||||
|
||||
let ret = {};
|
||||
|
||||
for (let url in urlToGroups) {
|
||||
let groups = urlToGroups[url];
|
||||
let item = byUrl[url];
|
||||
|
||||
if (groups.length === 0) {
|
||||
// Not filtered out but also not categorized
|
||||
groups = ['other'];
|
||||
}
|
||||
|
||||
for (let group of groups) {
|
||||
ret[group] ??= [];
|
||||
ret[group].push(item);
|
||||
|
||||
if (!ret[group].meta) {
|
||||
if (group === 'other') {
|
||||
ret[group].meta = { title: other };
|
||||
} else {
|
||||
ret[group].meta = grouping.getGroupMeta(group);
|
||||
ret[group].meta.title = titles[group] ?? ret[group].meta.title ?? capitalize(group);
|
||||
}
|
||||
for (let tag of tags) {
|
||||
if (item.data.tags.includes(tag)) {
|
||||
ret[tag].push(item);
|
||||
categorized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (other === false) {
|
||||
delete ret.other;
|
||||
}
|
||||
|
||||
// Sort
|
||||
let sortedGroups = groups ?? grouping.sortGroups?.(Object.keys(ret));
|
||||
|
||||
if (sortedGroups) {
|
||||
ret = sortObject(ret, sortedGroups);
|
||||
}
|
||||
|
||||
Object.defineProperty(ret, 'meta', {
|
||||
value: {
|
||||
groupCount: Object.keys(ret).length,
|
||||
},
|
||||
enumerable: false,
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort an object by its keys
|
||||
* @param {*} obj
|
||||
* @param {function | string[]} order
|
||||
*/
|
||||
function sortObject(obj, order) {
|
||||
let ret = {};
|
||||
let sortedKeys = Array.isArray(order) ? order : Object.keys(obj).sort(order);
|
||||
|
||||
for (let key of sortedKeys) {
|
||||
if (key in obj) {
|
||||
ret[key] = obj[key];
|
||||
if (!categorized) {
|
||||
ret.other.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
// Add any keys that weren't in the order
|
||||
for (let key in obj) {
|
||||
if (!(key in ret)) {
|
||||
ret[key] = obj[key];
|
||||
// Remove empty categories
|
||||
for (let category in ret) {
|
||||
if (ret[category].length === 0) {
|
||||
delete ret[category];
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function capitalize(str) {
|
||||
str += '';
|
||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||
export function getCategoryTitle(category, categories) {
|
||||
let title;
|
||||
if (Array.isArray(categories)) {
|
||||
// Find relevant entry
|
||||
// [{id: "Title"}, id2, ...]
|
||||
title = categories.find(entry => typeof entry === 'object' && entry?.[category])?.[category];
|
||||
} else if (typeof categories === 'object') {
|
||||
// {id: "Title", id2: "Title 2", ...}
|
||||
title = categories[category];
|
||||
}
|
||||
|
||||
if (title) {
|
||||
return title;
|
||||
}
|
||||
|
||||
// Capitalized
|
||||
return category.charAt(0).toUpperCase() + category.slice(1);
|
||||
}
|
||||
|
||||
const IDENTITY = x => x;
|
||||
|
||||
@@ -13,33 +13,23 @@ sidebar.palettes = {
|
||||
sidebar.updateCurrent();
|
||||
},
|
||||
|
||||
saved: [],
|
||||
|
||||
/**
|
||||
* Update saved palettes from local storage
|
||||
*/
|
||||
fromLocalStorage() {
|
||||
// Replace contents of array without breaking references
|
||||
let saved = localStorage.savedPalettes ? JSON.parse(localStorage.savedPalettes) : [];
|
||||
this.saved.splice(0, this.saved.length, ...saved);
|
||||
updateSaved() {
|
||||
this.saved = localStorage.savedPalettes ? JSON.parse(localStorage.savedPalettes) : [];
|
||||
},
|
||||
|
||||
/**
|
||||
* Write palettes to local storage
|
||||
*/
|
||||
toLocalStorage() {
|
||||
if (this.saved.length > 0) {
|
||||
localStorage.savedPalettes = JSON.stringify(this.saved);
|
||||
save(saved = this.saved) {
|
||||
this.saved = saved ?? [];
|
||||
|
||||
if (saved.length > 0) {
|
||||
localStorage.savedPalettes = JSON.stringify(saved);
|
||||
} else {
|
||||
delete localStorage.savedPalettes;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
sidebar.palettes.fromLocalStorage();
|
||||
|
||||
// Palettes were updated in another tab
|
||||
addEventListener('storage', () => sidebar.palettes.fromLocalStorage());
|
||||
sidebar.palettes.updateSaved();
|
||||
addEventListener('storage', event => sidebar.palettes.updateSaved());
|
||||
|
||||
sidebar.palette = {
|
||||
getUid() {
|
||||
@@ -69,9 +59,7 @@ sidebar.palette = {
|
||||
delete(palette) {
|
||||
let savedPalettes = sidebar.palettes.saved;
|
||||
let count = savedPalettes.length;
|
||||
|
||||
if (count === 0 || !palette.uid) {
|
||||
// No stored palettes or this palette has not been saved
|
||||
if (count === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -80,9 +68,7 @@ sidebar.palette = {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let index; (index = savedPalettes.findIndex(p => p.uid === palette.uid)) > -1; ) {
|
||||
savedPalettes.splice(index, 1);
|
||||
}
|
||||
savedPalettes = savedPalettes.filter(p => !sidebar.palette.equals(palette, p));
|
||||
|
||||
if (savedPalettes.length === count) {
|
||||
// Nothing was removed
|
||||
@@ -110,14 +96,17 @@ sidebar.palette = {
|
||||
|
||||
sidebar.updateCurrent();
|
||||
|
||||
sidebar.palettes.toLocalStorage();
|
||||
sidebar.palettes.save(savedPalettes);
|
||||
|
||||
if (globalThis.paletteApp?.saved?.uid === palette.uid) {
|
||||
// We deleted the currently active palette
|
||||
if (sidebar.palette.equals(globalThis.paletteApp?.saved, palette)) {
|
||||
paletteApp.postDelete();
|
||||
}
|
||||
},
|
||||
|
||||
getSaved(palette, savedPalettes = sidebar.palettes.saved) {
|
||||
return savedPalettes.find(p => sidebar.palette.equals(p, palette));
|
||||
},
|
||||
|
||||
render(palette) {
|
||||
// Find existing <a>
|
||||
let { title, id, search, uid } = palette;
|
||||
@@ -157,27 +146,23 @@ sidebar.palette = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Save a palette, either by updating its existing entry or creating a new one
|
||||
* @param {object} palette
|
||||
*/
|
||||
save(palette) {
|
||||
if (!palette.uid) {
|
||||
// First time saving
|
||||
palette.uid = this.getUid();
|
||||
}
|
||||
|
||||
save(palette, saved) {
|
||||
let savedPalettes = sidebar.palettes.saved;
|
||||
let existingIndex = palette.uid ? sidebar.palettes.saved.findIndex(p => p.uid === palette.uid) : -1;
|
||||
let newIndex = existingIndex > -1 ? existingIndex : savedPalettes.length;
|
||||
let existing = this.getSaved(saved ?? palette, savedPalettes);
|
||||
let oldValues;
|
||||
|
||||
let [oldValues] = sidebar.palettes.saved.splice(newIndex, 1, palette);
|
||||
if (existing) {
|
||||
// Rename
|
||||
oldValues = { ...existing };
|
||||
Object.assign(existing, palette);
|
||||
} else {
|
||||
savedPalettes.push(palette);
|
||||
}
|
||||
|
||||
this.render(palette, oldValues);
|
||||
sidebar.updateCurrent();
|
||||
sidebar.palettes.toLocalStorage();
|
||||
|
||||
return palette;
|
||||
sidebar.palettes.save(savedPalettes);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
* Get import code for remixed themes and tweaked palettes.
|
||||
*/
|
||||
export { getThemeCode } from './tweak/code.js';
|
||||
export { HUE_RANGES, cdnUrl, hues, selectors, tints, urls } from './tweak/data.js';
|
||||
export { cdnUrl, hueRanges, hues, selectors, tints, urls } from './tweak/data.js';
|
||||
export { default as Permalink } from './tweak/permalink.js';
|
||||
|
||||
@@ -12,6 +12,49 @@ export const urls = {
|
||||
typography: id => `styles/themes/${id}/typography.css`,
|
||||
};
|
||||
|
||||
export const selectors = {
|
||||
palette: id =>
|
||||
[':where(:root)', ':host', ":where([class^='wa-theme-'], [class*=' wa-theme-'])", `.wa-palette-${id}`].join(',\n'),
|
||||
};
|
||||
|
||||
export const hueRanges = {
|
||||
red: { min: 5, max: 35 }, // 30
|
||||
orange: { min: 35, max: 60 }, // 25
|
||||
yellow: { min: 60, max: 112 }, // 45
|
||||
green: { min: 112, max: 170 }, // 55
|
||||
cyan: { min: 170, max: 220 }, // 50
|
||||
blue: { min: 220, max: 265 }, // 45
|
||||
indigo: { min: 265, max: 290 }, // 25
|
||||
purple: { min: 290, max: 320 }, // 30
|
||||
pink: { min: 320, max: 365 }, // 45
|
||||
};
|
||||
|
||||
export const moreHue = {
|
||||
red: 'Redder',
|
||||
orange: 'More orange', // https://www.reddit.com/r/grammar/comments/u9n0uo/is_it_oranger_or_more_orange/
|
||||
yellow: 'Yellower',
|
||||
green: 'Greener',
|
||||
cyan: 'More cyan',
|
||||
blue: 'Bluer',
|
||||
indigo: 'More indigo',
|
||||
pink: 'Pinker',
|
||||
};
|
||||
|
||||
/**
|
||||
* Max gray chroma (% of chroma of undertone) per hue
|
||||
*/
|
||||
export const maxGrayChroma = {
|
||||
red: 0.2,
|
||||
orange: 0.2,
|
||||
yellow: 0.25,
|
||||
green: 0.25,
|
||||
cyan: 0.3,
|
||||
blue: 0.35,
|
||||
indigo: 0.35,
|
||||
purple: 0.3,
|
||||
pink: 0.25,
|
||||
};
|
||||
|
||||
export const docsURLs = {
|
||||
colors: '/docs/themes/',
|
||||
palette: '/docs/palettes/',
|
||||
@@ -25,189 +68,6 @@ export const icons = {
|
||||
typography: 'font-case',
|
||||
};
|
||||
|
||||
export const selectors = {
|
||||
palette: id =>
|
||||
[':where(:root)', ':host', ":where([class^='wa-theme-'], [class*=' wa-theme-'])", `.wa-palette-${id}`].join(',\n'),
|
||||
};
|
||||
export const hues = Object.keys(hueRanges);
|
||||
|
||||
export const HUE_RANGES = {
|
||||
red: { min: 15, max: 35 }, // 20
|
||||
orange: { min: 35, max: 75 }, // 40
|
||||
yellow: { min: 75, max: 110 }, // 35
|
||||
green: { min: 115, 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: 375 }, // 55
|
||||
};
|
||||
|
||||
export const hues = Object.keys(HUE_RANGES);
|
||||
export const allHues = [...hues, 'gray'];
|
||||
export const tints = ['05', '10', '20', '30', '40', '50', '60', '70', '80', '90', '95'];
|
||||
|
||||
export const L_RANGES = {
|
||||
'05': { min: 0.18, max: 0.2 },
|
||||
10: { min: 0.23, max: 0.25 },
|
||||
20: { min: 0.31, max: 0.35 },
|
||||
30: { min: 0.38, max: 0.43 },
|
||||
40: { min: 0.45, max: 0.5 },
|
||||
50: { min: 0.55, max: 0.6 },
|
||||
60: { min: 0.65, max: 0.7 },
|
||||
70: { min: 0.73, max: 0.78 },
|
||||
80: { min: 0.82, max: 0.85 },
|
||||
90: { min: 0.91, max: 0.93 },
|
||||
95: { min: 0.95, max: 0.97 },
|
||||
};
|
||||
|
||||
for (let range of [HUE_RANGES, L_RANGES]) {
|
||||
for (let key in range) {
|
||||
range[key].mid = (range[key].min + range[key].max) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Most common tint per hue.
|
||||
* Largely the statistical mode, but also informed by the average and median.
|
||||
*/
|
||||
export const HUE_TOP_TINT = {
|
||||
red: 50,
|
||||
orange: 70,
|
||||
yellow: 80,
|
||||
green: 80,
|
||||
cyan: 70,
|
||||
blue: 50,
|
||||
indigo: 40,
|
||||
purple: 50,
|
||||
pink: 50,
|
||||
gray: 40,
|
||||
};
|
||||
|
||||
/*
|
||||
┌─────────┬──────┬──────┬────────┬──────┬────────┬───────┐
|
||||
│ (index) │ min │ max │ median │ avg │ stddev │ count │
|
||||
├─────────┼──────┼──────┼────────┼──────┼────────┼───────┤
|
||||
│ red │ 0.74 │ 1 │ 0.92 │ 0.88 │ 0.085 │ 9 │
|
||||
│ yellow │ 0.72 │ 1 │ 0.98 │ 0.92 │ 0.11 │ 8 │
|
||||
│ green │ 0.55 │ 0.93 │ 0.75 │ 0.75 │ 0.1 │ 8 │
|
||||
│ cyan │ 0.7 │ 0.88 │ 0.82 │ 0.81 │ 0.053 │ 8 │
|
||||
│ blue │ 0.54 │ 1 │ 0.83 │ 0.82 │ 0.15 │ 9 │
|
||||
│ indigo │ 0.63 │ 1 │ 0.87 │ 0.86 │ 0.13 │ 8 │
|
||||
│ purple │ 0.58 │ 0.99 │ 0.86 │ 0.84 │ 0.11 │ 8 │
|
||||
│ pink │ 0.74 │ 1 │ 0.93 │ 0.89 │ 0.089 │ 8 │
|
||||
└─────────┴──────┴──────┴────────┴──────┴────────┴───────┘
|
||||
*/
|
||||
/** Max(Average, Median) % of max P3 chroma per hue, relative to palette maximum and capped to 0.8 */
|
||||
export const HUE_CHROMA_SCALE = {
|
||||
red: 0.92,
|
||||
orange: 0.96, // interpolated
|
||||
yellow: 1,
|
||||
green: 0.7,
|
||||
cyan: 0.81,
|
||||
blue: 0.83,
|
||||
indigo: 0.87,
|
||||
purple: 0.86,
|
||||
pink: 0.92,
|
||||
};
|
||||
|
||||
export const CHROMA_SCALE_LIGHTEST = {
|
||||
95: 1,
|
||||
90: 0.8,
|
||||
80: 0.5,
|
||||
70: 0.2,
|
||||
60: 0.2,
|
||||
50: 0.15,
|
||||
40: 0.1,
|
||||
};
|
||||
|
||||
export const MAX_CHROMA_BY_TINT = {
|
||||
95: 0.11,
|
||||
};
|
||||
|
||||
/**
|
||||
* Chroma levels to identify gray.
|
||||
* First number: below this we identify as gray regardless
|
||||
* Second number: below this we identify as gray if it's also in the bottom 25% of colors when sorted by chroma
|
||||
*/
|
||||
export const GRAY_CHROMA_BY_TINT = {
|
||||
'05': [0.03, 0.05],
|
||||
10: [0.035, 0.06],
|
||||
20: [0.045, 0.06],
|
||||
30: [0.05, 0.06],
|
||||
40: [0.05, 0.06],
|
||||
50: [0.04, 0.06],
|
||||
60: [0.03, 0.05],
|
||||
70: [0.02, 0.04],
|
||||
80: [0.015, 0.03],
|
||||
90: [0.007, 0.01],
|
||||
95: [0.004, 0.005],
|
||||
};
|
||||
|
||||
export const moreHue = {
|
||||
red: 'Redder',
|
||||
orange: 'More orange', // https://www.reddit.com/r/grammar/comments/u9n0uo/is_it_oranger_or_more_orange/
|
||||
yellow: 'Yellower',
|
||||
green: 'Greener',
|
||||
cyan: 'More cyan',
|
||||
blue: 'Bluer',
|
||||
indigo: 'More indigo',
|
||||
purple: 'Purpler',
|
||||
pink: 'Pinker',
|
||||
};
|
||||
|
||||
export const hueBefore = {};
|
||||
export const hueAfter = {};
|
||||
|
||||
for (let i = 0; i < hues.length; i++) {
|
||||
hueBefore[hues[i]] = hues[i - 1] ?? hues.at(-1);
|
||||
hueAfter[hues[i]] = hues[i + 1] ?? hues[0];
|
||||
}
|
||||
|
||||
export const HUE_SHIFTS = [
|
||||
// Reds
|
||||
{ range: [0, 25], peak: [10, 25], shift: { dark: 15, light: -18 }, maxConsecutive: { dark: 4, light: -2 } },
|
||||
// Yellows
|
||||
{ range: [30, 112], peak: [70, 100], shift: { dark: -48, light: 16 }, maxConsecutive: { dark: -20, light: 4 } },
|
||||
|
||||
// Greens
|
||||
{ range: [140, 160], peak: [145, 155], shift: { dark: 15, light: -5 }, maxConsecutive: { dark: 7, light: -5 } },
|
||||
// Blues
|
||||
{ range: [240, 265], peak: [245, 260], shift: { dark: -3, light: -15 }, maxConsecutive: { dark: -3, light: -4 } },
|
||||
];
|
||||
|
||||
export const CHROMA_CURVES = {
|
||||
50: { dark: 0.9, light: 0.8 },
|
||||
60: { dark: 1, light: 1.2 },
|
||||
70: { light: 1.2 },
|
||||
80: { dark: 1.1, light: 2 },
|
||||
90: { dark: 3, light: 2 },
|
||||
};
|
||||
|
||||
export const MAX_CHROMA_BOUNDS = { min: 0.08, max: 0.3 };
|
||||
|
||||
/**
|
||||
* Max gray chroma (% of chroma of undertone) per hue
|
||||
*/
|
||||
export const MAX_GRAY_CHROMA_SCALE = {
|
||||
red: 0.2,
|
||||
orange: 0.2,
|
||||
yellow: 0.25,
|
||||
green: 0.25,
|
||||
cyan: 0.3,
|
||||
blue: 0.35,
|
||||
indigo: 0.35,
|
||||
purple: 0.3,
|
||||
pink: 0.25,
|
||||
};
|
||||
|
||||
/** Default accent tint if all chromas are 0, but also the tint accent colors will be nudged towards (see chromaTolerance) */
|
||||
export const DEFAULT_ACCENT = 60;
|
||||
|
||||
/** Min and max allowed tints */
|
||||
export const MIN_ACCENT = 40;
|
||||
export const MAX_ACCENT = 90;
|
||||
|
||||
/** Chroma tolerance: Chroma will need to differ more than this to gravitate away from defaultAccent */
|
||||
export const CHROMA_TOLERANCE = 0.000001;
|
||||
|
||||
export const ROLES = ['brand', 'neutral', 'success', 'warning', 'danger'];
|
||||
|
||||
@@ -13,42 +13,56 @@ export default class Permalink extends URLSearchParams {
|
||||
return Object.fromEntries(this.entries());
|
||||
}
|
||||
|
||||
set(key, value, defaultValue) {
|
||||
if (equals(value, defaultValue) || equals(value, '')) {
|
||||
value = null;
|
||||
#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);
|
||||
}
|
||||
}
|
||||
|
||||
value ??= null; // undefined -> null
|
||||
writeTo(obj) {
|
||||
let mapping = this.#mappings.get(obj) ?? {};
|
||||
let { keyTo = IDENTITY, valueTo = IDENTITY, canExtend = false } = mapping;
|
||||
|
||||
let oldValue = Array.isArray(value) ? this.getAll(key) : this.get(key);
|
||||
let changed = !equals(value, oldValue);
|
||||
for (let [key, value] of this) {
|
||||
let mappedKey = keyTo(key);
|
||||
let mappedValue = valueTo(value);
|
||||
|
||||
if (!changed) {
|
||||
// Nothing to do here
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
super.delete(key);
|
||||
value = value.slice();
|
||||
|
||||
for (let v of value) {
|
||||
if (v || v === 0) {
|
||||
if (typeof v === 'object') {
|
||||
super.append(key, JSON.stringify(v));
|
||||
} else {
|
||||
super.append(key, v);
|
||||
}
|
||||
}
|
||||
if (canExtend || mappedKey in obj) {
|
||||
obj[mappedKey] = mappedValue;
|
||||
}
|
||||
} else if (value === null) {
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
this.changed ||= changed;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,40 +79,3 @@ export default class Permalink extends URLSearchParams {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function equals(value, oldValue) {
|
||||
if (Array.isArray(value) || Array.isArray(oldValue)) {
|
||||
value = toArray(value);
|
||||
oldValue = toArray(oldValue);
|
||||
|
||||
if (value.length !== oldValue.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return value.every((v, i) => equals(v, oldValue[i]));
|
||||
}
|
||||
|
||||
// (value ?? oldValue ?? true) returns true if they're both empty (null or undefined)
|
||||
[value, oldValue] = [value, oldValue].map(v => (!v && v !== false && v !== 0 ? null : v));
|
||||
return value === oldValue || String(value) === String(oldValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a value to an array. `undefined` and `null` values are converted to an empty array.
|
||||
* @param {*} value - The value to convert.
|
||||
* @returns {any[]} The converted array.
|
||||
*/
|
||||
function toArray(value) {
|
||||
value ??= [];
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// Don't convert "foo" into ["f", "o", "o"]
|
||||
if (typeof value !== 'string' && typeof value[Symbol.iterator] === 'function') {
|
||||
return Array.from(value);
|
||||
}
|
||||
|
||||
return [value];
|
||||
}
|
||||
|
||||
@@ -1,304 +1,36 @@
|
||||
// https://lea.verou.me/blog/2016/12/resolve-promises-externally-with-this-one-weird-trick/
|
||||
export function promise() {
|
||||
let res, rej;
|
||||
|
||||
let promise = new Promise((resolve, reject) => {
|
||||
res = resolve;
|
||||
rej = reject;
|
||||
});
|
||||
|
||||
return Object.assign(promise, { resolve: res, reject: rej });
|
||||
}
|
||||
|
||||
export function normalizeAngles(angles) {
|
||||
// First, normalize each angle individually
|
||||
let normalizedAngles = angles.map(h => ((h % 360) + 360) % 360);
|
||||
// First, normalize
|
||||
angles = angles.map(h => ((h % 360) + 360) % 360);
|
||||
|
||||
for (let i = 1; i < angles.length; i++) {
|
||||
let angle = normalizedAngles[i];
|
||||
let prevAngle = normalizedAngles[i - 1];
|
||||
let delta = angle - prevAngle;
|
||||
// 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 = [angle + 360, angle - 360];
|
||||
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;
|
||||
|
||||
// Offset hue to minimize difference in the direction that brings it closer to the previous hue
|
||||
let deltas = equivalent.map(e => Math.abs(e - prevAngle));
|
||||
|
||||
normalizedAngles[i] = equivalent[deltas[0] < deltas[1] ? 0 : 1];
|
||||
if (Math.abs(equivalent[0] - prevHue) <= Math.abs(equivalent[1] - prevHue)) {
|
||||
angles[i] = equivalent[0];
|
||||
} else {
|
||||
angles[i] = equivalent[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return normalizedAngles;
|
||||
return angles;
|
||||
}
|
||||
|
||||
export function subtractAngles(θ1, θ2) {
|
||||
let [a, b] = normalizeAngles([θ1, θ2]);
|
||||
return a - b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an object of keys to ranges, find the closest range.
|
||||
* Ranges are assumed to be mutually exclusive.
|
||||
* @param {Object<string, {min: number, max: number}>} ranges
|
||||
* @param {number} value
|
||||
* @param {object} options
|
||||
* @param {"angle" | undefined} options.type
|
||||
* @param {number} [options.tolerance=Infinity] If value is not within any range, how close can it be?
|
||||
* @param {(range: {min: number, max: number}) => {min: number, max: number}} options.getRange
|
||||
* @returns {{key: string, distance: number}} The key of the closest range. Distance is 0 if the value is within the range, negative if below, positive if above.
|
||||
*/
|
||||
export function getRange(ranges, value, options) {
|
||||
let { type } = options || {};
|
||||
let keys = Object.keys(ranges);
|
||||
let closest = { key: keys[0], distance: Infinity };
|
||||
|
||||
for (let key of keys) {
|
||||
let range = ranges[key];
|
||||
|
||||
if (options?.getRange) {
|
||||
range = options.getRange(range);
|
||||
}
|
||||
|
||||
let { min, max } = range;
|
||||
|
||||
if (Array.isArray(range)) {
|
||||
[min, max] = range;
|
||||
}
|
||||
|
||||
let deltaMin = type === 'angle' ? subtractAngles(value, min) : value - min;
|
||||
let deltaMax = type === 'angle' ? subtractAngles(value, max) : value - max;
|
||||
|
||||
if (deltaMin >= 0 && deltaMax <= 0) {
|
||||
return { key, distance: 0 };
|
||||
}
|
||||
|
||||
if (Math.abs(deltaMin) < Math.abs(closest.distance)) {
|
||||
closest = { key, distance: deltaMin };
|
||||
}
|
||||
|
||||
if (deltaMax > 0 && Math.abs(deltaMax) < Math.abs(closest.distance)) {
|
||||
closest = { key, distance: deltaMax };
|
||||
}
|
||||
}
|
||||
|
||||
// TODO use angle functions to check tolerance against angles
|
||||
if (options?.tolerance !== undefined && Math.abs(closest.distance) > options.tolerance) {
|
||||
return;
|
||||
}
|
||||
|
||||
return closest;
|
||||
}
|
||||
|
||||
export function camelCase(str) {
|
||||
return (str + '').replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
||||
}
|
||||
|
||||
export function capitalize(str) {
|
||||
if (!str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
str = str + '';
|
||||
return str[0].toUpperCase() + str.slice(1);
|
||||
}
|
||||
|
||||
export function arrayNext(array, element) {
|
||||
let index = array.indexOf(element);
|
||||
return array[(index + 1) % array.length];
|
||||
}
|
||||
|
||||
export function arrayPrevious(array, element) {
|
||||
let index = array.indexOf(element);
|
||||
return array[(index - 1 + array.length) % array.length];
|
||||
}
|
||||
|
||||
export function levelToIndex(level) {
|
||||
if (level === '05') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return level === '95' ? 10 : +level / 10;
|
||||
}
|
||||
|
||||
export function indexToLevel(i) {
|
||||
if (i === 0) {
|
||||
return '05';
|
||||
}
|
||||
|
||||
return (i === 10 ? 95 : i * 10) + '';
|
||||
}
|
||||
|
||||
export function previousLevel(level) {
|
||||
if (level === '05') {
|
||||
return;
|
||||
}
|
||||
|
||||
return indexToLevel(levelToIndex(level) - 1);
|
||||
}
|
||||
|
||||
export function nextLevel(level) {
|
||||
if (level === '95') {
|
||||
return;
|
||||
}
|
||||
|
||||
return indexToLevel(levelToIndex(level) + 1);
|
||||
}
|
||||
|
||||
export function relativeLevel(level, steps) {
|
||||
if (level == 100) {
|
||||
// loose intentional
|
||||
return relativeLevel(95, ++steps);
|
||||
}
|
||||
|
||||
if (level == 95) {
|
||||
// loose intentional
|
||||
return relativeLevel(90, ++steps);
|
||||
}
|
||||
|
||||
if (level == 0) {
|
||||
// loose intentional
|
||||
return relativeLevel(5, --steps);
|
||||
}
|
||||
|
||||
if (level == 5) {
|
||||
// loose intentional
|
||||
return relativeLevel(10, --steps);
|
||||
}
|
||||
|
||||
let index = clamp(0, levelToIndex(level) + steps, 10);
|
||||
|
||||
return indexToLevel(index);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} p Number from 0-1 where 0 is start and 1 is end
|
||||
* @param {*} start Number for p=0
|
||||
* @param {*} end Number for p=1
|
||||
* @returns
|
||||
*/
|
||||
export function interpolate(p, range = [0, 1], options) {
|
||||
let [start, end] = range;
|
||||
|
||||
if (p <= 0 || p >= 1 || range.length === 2) {
|
||||
let value = start + p * (end - start);
|
||||
return options?.unclamped ? value : clamp(start, value, end);
|
||||
}
|
||||
|
||||
// If we're here, there are more points in the range
|
||||
let interval = 1 / (range.length - 1);
|
||||
let index = Math.floor(p / interval);
|
||||
let intervalProgress = progress(p, [index * interval, (index + 1) * interval]);
|
||||
return interpolate(intervalProgress, range.slice(index, index + 2), options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inverse of interpolate: given a value, find the progress between start and end.
|
||||
* @param {*} value
|
||||
* @param {*} range
|
||||
* @returns
|
||||
*/
|
||||
export function progress(value, range = [0, 1], options) {
|
||||
let [start, end] = range;
|
||||
|
||||
if (value <= start || value >= end || range.length === 2) {
|
||||
let ret = (value - start) / (end - start);
|
||||
|
||||
return options?.unclamped ? ret : clamp(0, ret, 1);
|
||||
}
|
||||
|
||||
// If we're here, there are more points in the range
|
||||
let index = range.findIndex((v, i) => value > range[i - 1] && value <= v);
|
||||
return (index - 1) / (range.length - 1);
|
||||
}
|
||||
|
||||
export function mapRange(value, { from, to, progression }) {
|
||||
let p = progress(value, from);
|
||||
|
||||
if (progression) {
|
||||
p = progression(p);
|
||||
}
|
||||
|
||||
return interpolate(p, to);
|
||||
}
|
||||
|
||||
export function clamp(min, value, max) {
|
||||
if (max < min) {
|
||||
[min, max] = [max, min];
|
||||
}
|
||||
|
||||
if (min !== undefined) {
|
||||
value = Math.max(min, value);
|
||||
}
|
||||
|
||||
if (max !== undefined) {
|
||||
value = Math.min(max, value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
export function clampAngle(min, value, max) {
|
||||
[min, value, max] = normalizeAngles([min, value, max]);
|
||||
return clamp(min, value, max);
|
||||
}
|
||||
|
||||
export function interpolateAngles(p, range) {
|
||||
range = normalizeAngles(range);
|
||||
return interpolate(p, range, { unclamped: true });
|
||||
}
|
||||
|
||||
export function progressAngle(angle, range) {
|
||||
[angle, ...range] = normalizeAngles([angle, ...range]);
|
||||
return progress(angle, range, { unclamped: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* Round a number to the nearest multiple of `roundTo` or to the closest number in an array of numbers
|
||||
* @param {number} value
|
||||
* @param {number | number[]} roundTo
|
||||
* @returns
|
||||
*/
|
||||
export function roundTo(value, roundTo = 1) {
|
||||
if (Array.isArray(roundTo)) {
|
||||
let closest = roundTo[0];
|
||||
let closestDistance = Math.abs(value - closest);
|
||||
|
||||
for (let candidate of roundTo) {
|
||||
let distance = Math.abs(value - candidate);
|
||||
|
||||
if (distance < closestDistance) {
|
||||
closest = candidate;
|
||||
closestDistance = distance;
|
||||
}
|
||||
}
|
||||
|
||||
return closest;
|
||||
}
|
||||
|
||||
let decimals = roundTo.toString().split('.')[1]?.length ?? 0;
|
||||
let ret = Math.round(value / roundTo) * roundTo;
|
||||
|
||||
if (decimals > 0) {
|
||||
// Eliminate IEEE 754 floating point errors
|
||||
ret = +ret.toFixed(decimals);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function slugify(str) {
|
||||
return str
|
||||
.normalize('NFD')
|
||||
.replace(/[\u0300-\u036f]/g, '') // Convert accented letters to ASCII
|
||||
.replace(/[^\w\s-]/g, '') // Remove remaining non-ASCII characters
|
||||
.trim()
|
||||
.replace(/\s+/g, '-') // Convert whitespace to hyphens
|
||||
.toLowerCase();
|
||||
}
|
||||
|
||||
export function log(...args) {
|
||||
console.log(...args);
|
||||
return args[0];
|
||||
}
|
||||
|
||||
@@ -440,14 +440,9 @@ wa-page > main:has(> .index-grid) {
|
||||
&.color {
|
||||
border-color: transparent;
|
||||
transition: background var(--wa-transition-slow);
|
||||
background:
|
||||
linear-gradient(var(--color-top, transparent) 0% 100%) top no-repeat border-box,
|
||||
linear-gradient(var(--color-bottom, transparent) 0% 100%) bottom no-repeat border-box var(--color,);
|
||||
background-position: top, bottom;
|
||||
background-size:
|
||||
var(--color-top-width, 100%) var(--color-top-height, 30%),
|
||||
var(--color-bottom-width, 100%) var(--color-bottom-height, 30%);
|
||||
color: var(--swatch-text-color);
|
||||
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);
|
||||
@@ -646,46 +641,3 @@ table.colors {
|
||||
max-height: 21lh;
|
||||
}
|
||||
}
|
||||
|
||||
.color-select {
|
||||
&.default::part(display-input) {
|
||||
opacity: 0.6;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
> small {
|
||||
margin-inline-start: var(--wa-space-xs);
|
||||
padding-block: 0 var(--wa-space-xs);
|
||||
}
|
||||
|
||||
&::part(combobox)::before,
|
||||
wa-option::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 1.2em;
|
||||
aspect-ratio: 1;
|
||||
margin-inline-end: var(--wa-space-xs);
|
||||
flex: none;
|
||||
border-radius: var(--wa-border-radius-m);
|
||||
background: var(--color);
|
||||
border: 1px solid var(--wa-color-surface-default);
|
||||
}
|
||||
|
||||
wa-option {
|
||||
white-space: nowrap;
|
||||
|
||||
&::before {
|
||||
width: 1em;
|
||||
margin-inline: var(--wa-space-xs);
|
||||
}
|
||||
|
||||
&::part(checked-icon) {
|
||||
order: 2;
|
||||
}
|
||||
}
|
||||
|
||||
.default-badge {
|
||||
opacity: 0.6;
|
||||
margin-inline-start: var(--wa-space-xs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,13 +19,13 @@ icon: card
|
||||
|
||||
<div slot="footer">
|
||||
<wa-button variant="brand" pill>More Info</wa-button>
|
||||
<wa-rating label="Rating"></wa-rating>
|
||||
<wa-rating></wa-rating>
|
||||
</div>
|
||||
</wa-card>
|
||||
|
||||
<style>
|
||||
.card-overview {
|
||||
width: 300px;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.card-overview small {
|
||||
|
||||
@@ -2,10 +2,13 @@
|
||||
title: Components
|
||||
description: Components are the essential building blocks to create intuitive, cohesive experiences. Browse the library of customizable, framework-friendly web components included in Web Awesome.
|
||||
layout: overview
|
||||
override:tags: []
|
||||
categories:
|
||||
tags: [actions, feedback, imagery, inputs, navigation, organization, helpers]
|
||||
titles:
|
||||
feedback: 'Feedback & Status'
|
||||
helpers: 'Utilities'
|
||||
- actions
|
||||
- feedback: 'Feedback & Status'
|
||||
- imagery
|
||||
- inputs
|
||||
- navigation
|
||||
- organization
|
||||
- helpers: 'Utilities'
|
||||
override:tags: []
|
||||
---
|
||||
|
||||
@@ -1,80 +1,10 @@
|
||||
/**
|
||||
* Global data for all pages
|
||||
*/
|
||||
import { sort } from '../_utils/filters.js';
|
||||
|
||||
export default {
|
||||
eleventyComputed: {
|
||||
// Default parent. Can be overridden by explicitly setting parent in the data.
|
||||
// parent can refer to either an ancestor page in the URL or another page in the same directory
|
||||
parent(data) {
|
||||
let { parent, page } = data;
|
||||
|
||||
if (parent) {
|
||||
return parent;
|
||||
}
|
||||
|
||||
return page.url.split('/').filter(Boolean).at(-2);
|
||||
},
|
||||
|
||||
parentUrl(data) {
|
||||
let { parent, page } = data;
|
||||
return getParentUrl(page.url, parent);
|
||||
},
|
||||
|
||||
parentItem(data) {
|
||||
let { parentUrl } = data;
|
||||
return data.collections.all.find(item => item.url === parentUrl);
|
||||
},
|
||||
|
||||
children(data) {
|
||||
let { collections, page, parentOf } = data;
|
||||
let mainTag = data.tags?.[0];
|
||||
let collection = data.collections[mainTag] ?? [];
|
||||
|
||||
if (parentOf) {
|
||||
return collections[parentOf];
|
||||
}
|
||||
|
||||
let collection = collections.all ?? [];
|
||||
let url = page.url;
|
||||
|
||||
let ret = collection.filter(item => {
|
||||
return item.data.parentUrl === url;
|
||||
});
|
||||
|
||||
sort(ret);
|
||||
|
||||
return ret;
|
||||
return collection.filter(item => item.data.parent === data.page.fileSlug);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
function getParentUrl(url, parent) {
|
||||
let parts = url.split('/').filter(Boolean);
|
||||
let ancestorIndex = parts.findLastIndex(part => part === parent);
|
||||
let retParts = parts.slice();
|
||||
|
||||
if (ancestorIndex > -1) {
|
||||
// parent is an ancestor
|
||||
retParts.splice(ancestorIndex + 1);
|
||||
} else {
|
||||
// parent is a sibling in the same directory
|
||||
retParts.splice(-1, 1, parent);
|
||||
}
|
||||
|
||||
let ret = retParts.join('/');
|
||||
|
||||
if (url.startsWith('/')) {
|
||||
ret = '/' + ret;
|
||||
}
|
||||
|
||||
if (!retParts.at(-1).includes('.') && !ret.endsWith('/')) {
|
||||
// If no extension, make sure to end with a slash
|
||||
ret += '/';
|
||||
}
|
||||
|
||||
if (ret === '/docs/') {
|
||||
ret = '/';
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
---
|
||||
title: Clamped Color Tokens
|
||||
title: Clamped brand tokens
|
||||
layout: block
|
||||
---
|
||||
|
||||
{% set tints = ['max-50', 'max-60', 'max-70', 'min-50', 'min-60', 'min-70'] %}
|
||||
{% set hues = ['red', 'orange', 'yellow', 'green', 'cyan', 'blue', 'indigo', 'purple', 'pink', 'gray'] %}
|
||||
{% 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>
|
||||
@@ -17,18 +20,18 @@ layout: block
|
||||
</tr>
|
||||
</thead>
|
||||
{% for hue in hues -%}
|
||||
<tr class="wa-color-{{ hue }}">
|
||||
<tr class="wa-brand-{{ hue }}">
|
||||
<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); --key: var(--wa-color-{{ hue }}-key);">
|
||||
<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-{{ hue }}" copy-label="--wa-color-{{ hue }}"></wa-copy-button>
|
||||
<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-{{ hue }}-{{ tint }})">
|
||||
<wa-copy-button value="--wa-color-{{ hue }}-{{ tint }}" copy-label="--wa-color-{{ hue }}-{{ tint }}"></wa-copy-button>
|
||||
<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 -%}
|
||||
@@ -38,7 +41,7 @@ layout: block
|
||||
|
||||
<style>
|
||||
.core-column .color.swatch::before {
|
||||
counter-reset: key var(--key);
|
||||
counter-reset: key var(--wa-color-brand-key);
|
||||
content: counter(key);
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
title: Layout
|
||||
description: Layout components and utility classes help you organize content that can adapt to any device or screen size. See the [installation instructions](#installation) to use Web Awesome's layout tools in your project.
|
||||
layout: overview
|
||||
parentOf: layout
|
||||
categories: ["components", "utilities"]
|
||||
override:tags: []
|
||||
---
|
||||
@@ -23,4 +22,4 @@ Or, you can choose to import _only_ the utilities:
|
||||
```html
|
||||
<link rel="stylesheet" href="{% cdnUrl 'styles/utilities.css' %}" />
|
||||
```
|
||||
{% endmarkdown %}
|
||||
{% endmarkdown %}
|
||||
@@ -1,28 +0,0 @@
|
||||
import { tints } from '/assets/scripts/tweak/data.js';
|
||||
|
||||
export function generateGrays(colors, { grayColor, grayChroma, grayLevel }) {
|
||||
let ret = {};
|
||||
let undertoneScale = colors[grayColor];
|
||||
|
||||
// These will be the same, since scaling them won't change the relationship
|
||||
ret.maxChromaTint = grayLevel ?? undertoneScale.maxChromaTint;
|
||||
Object.defineProperty(ret, 'core', {
|
||||
enumerable: false,
|
||||
get() {
|
||||
return this[this.maxChromaTint];
|
||||
},
|
||||
});
|
||||
ret.maxChromaTintRaw = undertoneScale.maxChromaTintRaw;
|
||||
|
||||
for (let tint of tints) {
|
||||
let colorUndertone = undertoneScale[tint].clone().to('oklch');
|
||||
ret[tint] = colorUndertone.set({ c: c => c * grayChroma });
|
||||
}
|
||||
|
||||
ret.maxChroma = ret[ret.maxChromaTint].get('oklch.c');
|
||||
ret.maxChromaRaw = ret[ret.maxChromaTintRaw].get('oklch.c');
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
export default generateGrays;
|
||||
@@ -1,162 +0,0 @@
|
||||
// TODO move these to local imports
|
||||
import Color from 'https://colorjs.io/dist/color.js';
|
||||
import generateGrays from './generate-grays.js';
|
||||
import generateScale from './generate-scale.js';
|
||||
import getMaxChroma from './get-max-chroma.js';
|
||||
import { getCoreTint } from './util.js';
|
||||
import {
|
||||
HUE_CHROMA_SCALE,
|
||||
HUE_RANGES,
|
||||
HUE_TOP_TINT,
|
||||
L_RANGES,
|
||||
MAX_ACCENT,
|
||||
MIN_ACCENT,
|
||||
} from '/assets/scripts/tweak/data.js';
|
||||
import {
|
||||
clamp,
|
||||
clampAngle,
|
||||
interpolate,
|
||||
normalizeAngles,
|
||||
progressAngle,
|
||||
roundTo,
|
||||
subtractAngles,
|
||||
} from '/assets/scripts/tweak/util.js';
|
||||
|
||||
export default function generatePalette(seedHues, { huesAfter: allHuesAfter, ...options } = {}) {
|
||||
let ret = {};
|
||||
|
||||
// Generate scales from seed hues
|
||||
let firstSeedHue;
|
||||
|
||||
let coreLevels = {};
|
||||
let seedMeta = {};
|
||||
|
||||
for (let hue in seedHues) {
|
||||
let seedColors = seedHues[hue];
|
||||
|
||||
if (!seedColors) {
|
||||
continue;
|
||||
}
|
||||
|
||||
firstSeedHue ??= hue;
|
||||
|
||||
let coreLevel = (coreLevels[hue] = getCoreTint(seedColors));
|
||||
let coreColor = seedColors[coreLevel];
|
||||
let [l, c, h] = coreColor.getAll('oklch');
|
||||
|
||||
let lOffset = l - L_RANGES[coreLevel].mid;
|
||||
let cScale = c / getMaxChroma(l, h);
|
||||
let relativeCScale = cScale / HUE_CHROMA_SCALE[hue];
|
||||
let levelOffset = coreLevel - HUE_TOP_TINT[hue];
|
||||
seedMeta[hue] = { lOffset, cScale, relativeCScale, levelOffset };
|
||||
|
||||
ret[hue] = generateScale(seedColors);
|
||||
}
|
||||
|
||||
if (!firstSeedHue) {
|
||||
// No valid seed colors, abort mission
|
||||
return null;
|
||||
}
|
||||
|
||||
// Fill in remaining hues
|
||||
let hueBefore = firstSeedHue;
|
||||
|
||||
for (let hue of allHuesAfter[firstSeedHue]) {
|
||||
if (hue in ret) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let huesAfter = allHuesAfter[hue];
|
||||
let seedHuesAfter = huesAfter.filter(hue => seedHues[hue]);
|
||||
let neighboringSeedHues = [seedHuesAfter.at(-1), seedHuesAfter[0]];
|
||||
|
||||
// A number from 0 to 1 indicating how close we are to each neighboring seed hue (0 if only one seed hue)
|
||||
let hueProgress =
|
||||
seedHuesAfter.length === 1
|
||||
? 0
|
||||
: progressAngle(
|
||||
HUE_RANGES[hue].mid,
|
||||
neighboringSeedHues.map(hue => HUE_RANGES[hue].mid),
|
||||
);
|
||||
|
||||
// Hue of the core color of the previous seed scale
|
||||
let hBefore = ret[hueBefore][ret[hueBefore].maxChromaTint].get('oklch.h');
|
||||
|
||||
// We start from the midpoint of the hue range
|
||||
let h = HUE_RANGES[hue].mid;
|
||||
|
||||
// Shift if too close to seed hues
|
||||
let hBeforeDelta = subtractAngles(h, hBefore);
|
||||
|
||||
if (Math.abs(hBeforeDelta) < 40) {
|
||||
h = hBefore + 40 * Math.sign(hBeforeDelta);
|
||||
}
|
||||
|
||||
if (seedHuesAfter.length > 1) {
|
||||
let hueAfter = seedHuesAfter[0];
|
||||
let hAfter = ret[hueAfter][ret[hueAfter].maxChromaTint].get('oklch.h');
|
||||
[hBefore, h, hAfter] = normalizeAngles([hBefore, h, hAfter]);
|
||||
let hAfterDelta = subtractAngles(hAfter, h);
|
||||
|
||||
if (hAfter - 40 < hBefore + 40) {
|
||||
// It's not possible to have a distance of at least 40deg from both neighboring hues
|
||||
// so at least maximize distance
|
||||
h = (hBefore + hAfter) / 2;
|
||||
} else if (hAfterDelta < 40) {
|
||||
h = hAfter - 40;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure hue is still within range for this scale
|
||||
h = clampAngle(HUE_RANGES[hue].min, h, HUE_RANGES[hue].max);
|
||||
|
||||
let coreLevelOffset = interpolate(
|
||||
hueProgress,
|
||||
neighboringSeedHues.map(hue => seedMeta[hue].levelOffset),
|
||||
);
|
||||
let coreLevel = clamp(MIN_ACCENT, roundTo(HUE_TOP_TINT[hue] + coreLevelOffset, 10), MAX_ACCENT);
|
||||
|
||||
coreLevels[hue] = coreLevel;
|
||||
let lOffsets = neighboringSeedHues.map(hue => seedMeta[hue].lOffset);
|
||||
let lOffset = interpolate(hueProgress, lOffsets);
|
||||
let l = L_RANGES[coreLevel].mid + lOffset;
|
||||
|
||||
let cScale = 1;
|
||||
|
||||
if (hue === 'yellow') {
|
||||
// Yellow tends to be the brighest hue in the palette
|
||||
cScale = Math.max(
|
||||
...Object.values(seedMeta)
|
||||
.map(meta => meta.relativeCScale)
|
||||
.filter(c => c > 0),
|
||||
);
|
||||
} else {
|
||||
cScale = interpolate(
|
||||
hueProgress,
|
||||
neighboringSeedHues.map(neighboringHue => seedMeta[neighboringHue].relativeCScale),
|
||||
);
|
||||
}
|
||||
|
||||
cScale *= HUE_CHROMA_SCALE[hue];
|
||||
|
||||
let maxC = getMaxChroma(l, h);
|
||||
let c = cScale * maxC;
|
||||
// let c = interpolate(
|
||||
// hueProgress,
|
||||
// pinnedScale.map(scale => scale.maxChroma),
|
||||
// );
|
||||
|
||||
let coreColor = new Color('oklch', [l, c, h]).toGamut('p3');
|
||||
|
||||
ret[hue] = generateScale(coreColor);
|
||||
hueBefore = hue;
|
||||
}
|
||||
|
||||
if ('gray' in seedHues) {
|
||||
ret.gray = generateScale(seedHues.gray);
|
||||
} else {
|
||||
ret.gray = generateGrays(ret, options);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
import { getCoreTint, getHueShift, getLightness, identifyColor } from './util.js';
|
||||
import {
|
||||
CHROMA_CURVES,
|
||||
CHROMA_SCALE_LIGHTEST,
|
||||
L_RANGES,
|
||||
MAX_CHROMA_BY_TINT,
|
||||
tints,
|
||||
} from '/assets/scripts/tweak/data.js';
|
||||
import { clamp, interpolate, progress } from '/assets/scripts/tweak/util.js';
|
||||
|
||||
/**
|
||||
* Generate a scale of tints from one or more key colors
|
||||
* @param {Color | Record<number | string, Color>} seedColors
|
||||
* @returns {Record<number | string, Color>}
|
||||
*/
|
||||
export function generateScale(seedColors) {
|
||||
if (seedColors.constructor.name === 'Color') {
|
||||
// Single color given
|
||||
let { level } = identifyColor(seedColors);
|
||||
seedColors = { [level]: seedColors };
|
||||
}
|
||||
|
||||
// Find core color
|
||||
let coreLevel = getCoreTint(seedColors);
|
||||
let coreColor = seedColors[coreLevel];
|
||||
let coreChroma = coreColor.get('oklch.c');
|
||||
|
||||
let scale = {};
|
||||
|
||||
Object.defineProperties(scale, {
|
||||
maxChromaTint: { value: coreLevel, enumerable: false, configurable: true },
|
||||
maxChromaTintRaw: { value: coreLevel, enumerable: false, configurable: true },
|
||||
maxChroma: { value: coreChroma, enumerable: false, configurable: true },
|
||||
maxChromaRaw: { value: coreChroma, enumerable: false, configurable: true },
|
||||
core: {
|
||||
get() {
|
||||
return this[this.maxChromaTint];
|
||||
},
|
||||
enumerable: false,
|
||||
},
|
||||
});
|
||||
|
||||
// First, add pinned colors
|
||||
for (let tint in seedColors) {
|
||||
scale[tint] = seedColors[tint];
|
||||
}
|
||||
|
||||
// For finding lightest and darkest pinned colors
|
||||
let pinnedTints = Object.keys(seedColors).sort((a, b) => a - b);
|
||||
let chromaCurve = CHROMA_CURVES[clamp(50, coreLevel, 90)];
|
||||
|
||||
// Now generate the rest, starting from the edges
|
||||
if (!('95' in scale)) {
|
||||
let lightestPinnedTint = pinnedTints.at(-1);
|
||||
let lightest = seedColors[lightestPinnedTint];
|
||||
let lOffset = lightest.get('oklch.l') - L_RANGES[lightestPinnedTint].mid;
|
||||
let chromaScale = CHROMA_SCALE_LIGHTEST[lightestPinnedTint];
|
||||
let hueShift = getHueShift(lightest, lightestPinnedTint, '95');
|
||||
|
||||
let color = lightest.clone().to('oklch');
|
||||
color.set({
|
||||
l: getLightness(95, lOffset),
|
||||
c: clamp(0, lightest.get('oklch.c') * chromaScale, MAX_CHROMA_BY_TINT[95]),
|
||||
h: h => h + hueShift,
|
||||
});
|
||||
|
||||
scale[95] = color;
|
||||
}
|
||||
|
||||
if (!('05' in scale)) {
|
||||
let darkestPinnedTint = pinnedTints[0];
|
||||
let darkest = seedColors[darkestPinnedTint];
|
||||
let lOffset = darkest.get('oklch.l') - L_RANGES[darkestPinnedTint].mid;
|
||||
let color = darkest.clone().to('oklch');
|
||||
let hueShift = getHueShift(darkest, darkestPinnedTint, '05');
|
||||
|
||||
color.set({
|
||||
l: getLightness('05', lOffset),
|
||||
// TODO c
|
||||
h: h => h + hueShift,
|
||||
});
|
||||
|
||||
scale['05'] = color;
|
||||
}
|
||||
|
||||
let tintBefore = '05';
|
||||
|
||||
for (let tint of tints) {
|
||||
if (tint in scale) {
|
||||
// Pinned or already generated
|
||||
tintBefore = tint;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Generated color
|
||||
// First, find closest pinned colors before and after
|
||||
let tintAfter = pinnedTints.find(level => level > tint) ?? '95';
|
||||
let neighboringTints = [tintBefore, tintAfter];
|
||||
let neighboringColors = neighboringTints.map(t => scale[t]);
|
||||
let tintProgress = progress(tint, neighboringTints);
|
||||
|
||||
let color = coreColor.clone().to('oklch');
|
||||
|
||||
// Lightness
|
||||
let lOffset = interpolate(
|
||||
tintProgress,
|
||||
neighboringTints.map(t => scale[t].get('oklch.l') - L_RANGES[t].mid),
|
||||
);
|
||||
|
||||
// Interpolate hue linearly and chroma with a power curve
|
||||
color.set({
|
||||
l: getLightness(tint, lOffset),
|
||||
c: interpolate(
|
||||
tintProgress,
|
||||
neighboringColors.map(c => c.get('oklch.c')),
|
||||
{
|
||||
progression: tint > coreLevel ? p => p ** chromaCurve.light : undefined,
|
||||
},
|
||||
),
|
||||
h: interpolate(
|
||||
tintProgress,
|
||||
neighboringColors.map(c => c.get('oklch.h')),
|
||||
),
|
||||
});
|
||||
|
||||
scale[tint] = color;
|
||||
}
|
||||
|
||||
for (let tint in scale) {
|
||||
if (!(tint in seedColors) && scale[tint].toGamut) {
|
||||
scale[tint] = scale[tint].toGamut('p3');
|
||||
}
|
||||
}
|
||||
|
||||
return scale;
|
||||
}
|
||||
|
||||
export default generateScale;
|
||||
@@ -1,3 +0,0 @@
|
||||
export { generateGrays, generateGrays as grays } from './generate-grays.js';
|
||||
export { generatePalette, generatePalette as palette } from './generate-palette.js';
|
||||
export { generateScale, generateScale as scale } from './generate-scale.js';
|
||||
@@ -1,91 +0,0 @@
|
||||
/**
|
||||
* Memoized calculation of OKLCH gamut boundary for a given L and H
|
||||
* Currently unused, but we can use it if existing code becomes too slow.
|
||||
*/
|
||||
import Color from 'https://colorjs.io/dist/color.js';
|
||||
import { interpolate, progress, progressAngle, roundTo } from '/assets/scripts/tweak/util.js';
|
||||
|
||||
/** Max oklch.c per h and l (rounded to 1 significant digit) */
|
||||
const maxChroma = {};
|
||||
const OOG_CHROMA = 0.4; // guaranteed to be OOG for every P3 color
|
||||
const C_THRESHOLD = 0.03;
|
||||
const MIN_H_STEP = 0.1;
|
||||
const MIN_L_STEP = 0.001;
|
||||
|
||||
export default function getMaxChroma(l, h) {
|
||||
let { hStep, lStep, count } = calculateBoundary(l, h);
|
||||
|
||||
let hRounded = roundTo(h, hStep);
|
||||
let lRounded = roundTo(l, lStep);
|
||||
|
||||
// Calculate gamut boundary around this point
|
||||
let hProgress = progressAngle(h - hRounded, [-hStep, 0, hStep]);
|
||||
let lProgress = progress(l - lRounded, [-lStep, 0, lStep]);
|
||||
let maxChromaH = [];
|
||||
|
||||
for (let i of [-1, 0, 1]) {
|
||||
let h = roundTo(hRounded + i * hStep, hStep);
|
||||
|
||||
let cs = [-1, 0, 1].map(j => {
|
||||
let l = roundTo(lRounded + j * lStep, lStep);
|
||||
|
||||
return maxChroma[l][h];
|
||||
});
|
||||
|
||||
maxChromaH.push(interpolate(lProgress, cs));
|
||||
}
|
||||
|
||||
// Interpolate between the 9 points using bilinear interpolation
|
||||
let c = interpolate(hProgress, maxChromaH);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
function calculateBoundary(pointL, pointH, lStep = 0.1, hStep = 10) {
|
||||
let hRounded = roundTo(pointH, hStep);
|
||||
let lRounded = roundTo(pointL, lStep);
|
||||
let ret = { count: 0, hStep, lStep };
|
||||
|
||||
for (let i of [-1, 0, 1]) {
|
||||
let l = roundTo(lRounded + i * lStep, lStep);
|
||||
maxChroma[l] ??= {};
|
||||
|
||||
for (let j of [-1, 0, 1]) {
|
||||
let h = roundTo(hRounded + j * hStep, hStep);
|
||||
|
||||
if (maxChroma[l][h] !== undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let gamutBoundary = new Color('oklch', [l, OOG_CHROMA, h]).toGamut('p3', { method: 'oklch.c' });
|
||||
let c = gamutBoundary.get('c');
|
||||
maxChroma[l][h] = c;
|
||||
ret.count++;
|
||||
let tooFar = { h: false, l: false };
|
||||
|
||||
if (i > -1) {
|
||||
let lPrev = roundTo(lRounded + (i - 1) * lStep, lStep);
|
||||
let cPrev = maxChroma[lPrev][h];
|
||||
tooFar.l = Math.abs(c - cPrev) > C_THRESHOLD && lStep > MIN_L_STEP;
|
||||
|
||||
if (tooFar.l) {
|
||||
ret.lStep /= 2;
|
||||
ret.count += calculateBoundary(pointL, pointH, ret.lStep, ret.hStep).count;
|
||||
}
|
||||
}
|
||||
|
||||
if (j > -1) {
|
||||
let hPrev = roundTo(hRounded + (j - 1) * hStep, hStep);
|
||||
let cPrev = maxChroma[l][hPrev];
|
||||
tooFar.h = Math.abs(c - cPrev) > C_THRESHOLD && hStep > MIN_H_STEP;
|
||||
|
||||
if (tooFar.h) {
|
||||
ret.hStep /= 2;
|
||||
ret.count += calculateBoundary(pointL, pointH, ret.lStep, ret.hStep).count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
import { stringifyColor } from './util.js';
|
||||
import { cssImport, cssLiteral, cssRule } from '/assets/scripts/tweak/code.js';
|
||||
import { selectors, tints, urls } from '/assets/scripts/tweak/data.js';
|
||||
|
||||
export function getPaletteCode({ base, colors, tweaked, ...options }) {
|
||||
let imports = [];
|
||||
|
||||
if (base && options.imports !== false) {
|
||||
imports.push(urls.palette(base));
|
||||
}
|
||||
|
||||
let ret = imports.map(url => cssImport(url, options)).join('\n');
|
||||
|
||||
let declarations = [];
|
||||
let prefix = options.prefix ?? 'wa-color';
|
||||
|
||||
let css = '';
|
||||
|
||||
if (tweaked) {
|
||||
for (let hue in colors) {
|
||||
if (hue === 'gray') {
|
||||
if (!tweaked.grayChroma && !tweaked.grayColor) {
|
||||
continue;
|
||||
}
|
||||
} else if (!tweaked.chromaScale && !tweaked.hue?.[hue]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let scale = colors[hue];
|
||||
|
||||
for (let tint of tints) {
|
||||
let color = scale[tint];
|
||||
let stringified = stringifyColor(color);
|
||||
declarations.push(`--${prefix}-${hue}-${tint}: ${stringified};`);
|
||||
}
|
||||
|
||||
let coreTint = scale.maxChromaTint;
|
||||
if (coreTint) {
|
||||
declarations.push(
|
||||
`--${prefix}-${hue}: var(--${prefix}-${hue}-${coreTint});`,
|
||||
`--${prefix}-${hue}-key: ${coreTint};`,
|
||||
);
|
||||
}
|
||||
|
||||
declarations.push('');
|
||||
}
|
||||
}
|
||||
|
||||
if (declarations.length > 0) {
|
||||
let selector = options.selector ?? selectors.palette(base);
|
||||
css += cssRule(selector, declarations);
|
||||
}
|
||||
|
||||
if (css) {
|
||||
if (imports.length) {
|
||||
ret += '\n\n';
|
||||
}
|
||||
|
||||
ret += `${cssLiteral(css, options)}`;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
export default getPaletteCode;
|
||||
@@ -1,28 +0,0 @@
|
||||
// TODO move these to local imports
|
||||
import Color from 'https://colorjs.io/dist/color.js';
|
||||
import { tints } from '/assets/scripts/tweak/data.js';
|
||||
|
||||
let palettes = await fetch('/docs/palettes/data.json').then(r => r.json());
|
||||
|
||||
for (let palette in palettes) {
|
||||
for (let hue in palettes[palette].colors) {
|
||||
let scale = palettes[palette].colors[hue];
|
||||
for (let tint of tints) {
|
||||
let color = scale[tint];
|
||||
|
||||
if (Array.isArray(color)) {
|
||||
scale[tint] = new Color('oklch', color);
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(scale, 'core', {
|
||||
get() {
|
||||
return this[this.maxChromaTint];
|
||||
},
|
||||
enumerable: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
globalThis.allPalettes = palettes;
|
||||
export default palettes;
|
||||
@@ -1,75 +0,0 @@
|
||||
// TODO move these to local imports
|
||||
import generateGrays from './generate-grays.js';
|
||||
import { tints } from '/assets/scripts/tweak/data.js';
|
||||
|
||||
export function tweakPalette(baseColors, tweaks, tweaked) {
|
||||
let ret = {};
|
||||
|
||||
if (!tweaked) {
|
||||
return baseColors;
|
||||
}
|
||||
|
||||
for (let hue in baseColors) {
|
||||
let originalScale = baseColors[hue];
|
||||
let scale = (ret[hue] = {});
|
||||
let descriptors = Object.getOwnPropertyDescriptors(originalScale);
|
||||
Object.defineProperties(scale, {
|
||||
maxChromaTint: { ...descriptors.maxChromaTint, enumerable: false },
|
||||
maxChromaTintRaw: { ...descriptors.maxChromaTintRaw, enumerable: false },
|
||||
core: {
|
||||
get() {
|
||||
return this[this.maxChromaTint];
|
||||
},
|
||||
enumerable: false,
|
||||
},
|
||||
});
|
||||
|
||||
if (hue === 'gray') {
|
||||
if (tweaked.grayChroma || tweaked.grayColor) {
|
||||
let grayColor = tweaks.grayColor ?? this.originalGrayColor;
|
||||
let grayChroma = this.computedGrayChroma;
|
||||
let grayLevel = baseColors.gray?.maxChromaTint;
|
||||
ret.gray = generateGrays(baseColors, { grayColor, grayChroma, grayLevel });
|
||||
} else {
|
||||
ret.gray = originalScale;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let tint of tints) {
|
||||
scale[tint] = tweakColor(hue, originalScale[tint], tweaks, tweaked);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function tweakColor(hue, originalColor, tweaks, tweaked) {
|
||||
if (!tweaked) {
|
||||
return originalColor;
|
||||
}
|
||||
|
||||
let color = originalColor;
|
||||
let { hueShifts, chromaScale = 1, grayColor, grayChroma } = tweaks;
|
||||
|
||||
let tweak = {};
|
||||
let thisTweaked = false;
|
||||
|
||||
if (tweaked.hue && hueShifts[hue]) {
|
||||
tweak.h = h => h + hueShifts[hue];
|
||||
thisTweaked = true;
|
||||
}
|
||||
|
||||
if (tweaked.chromaScale && chromaScale !== 1) {
|
||||
tweak.c = c => c * chromaScale;
|
||||
thisTweaked = true;
|
||||
}
|
||||
|
||||
if (thisTweaked) {
|
||||
color = color.clone().to('oklch').set(tweak);
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
export default tweakPalette;
|
||||
@@ -1,154 +0,0 @@
|
||||
import {
|
||||
CHROMA_TOLERANCE,
|
||||
DEFAULT_ACCENT,
|
||||
GRAY_CHROMA_BY_TINT,
|
||||
HUE_RANGES,
|
||||
HUE_SHIFTS,
|
||||
L_RANGES,
|
||||
MAX_ACCENT,
|
||||
MIN_ACCENT,
|
||||
tints,
|
||||
} from '/assets/scripts/tweak/data.js';
|
||||
import { clamp, getRange, mapRange } from '/assets/scripts/tweak/util.js';
|
||||
|
||||
export function identifyColor(color, colors) {
|
||||
let [l, c, h] = color.getAll('oklch');
|
||||
let level = getRange(L_RANGES, l).key;
|
||||
let hue;
|
||||
|
||||
// Identify grays
|
||||
let grayBounds = GRAY_CHROMA_BY_TINT[level];
|
||||
if (c <= grayBounds[1]) {
|
||||
// Possibly gray
|
||||
if (c <= grayBounds[0]) {
|
||||
// Definitely gray
|
||||
hue = 'gray';
|
||||
} else if (colors) {
|
||||
// May or may not be gray, compare to palette max chroma
|
||||
// FIXME this does not take level into account, so is more likely to identify lighter colors as gray
|
||||
let maxChroma = Math.max(...colors.map(color => color.get('oklch.c')));
|
||||
|
||||
if (c / maxChroma < 0.2) {
|
||||
hue = 'gray';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hue ??= getRange(HUE_RANGES, h, { type: 'angle' }).key;
|
||||
|
||||
return { hue, level };
|
||||
}
|
||||
|
||||
export function getLightness(level, distance) {
|
||||
return clamp(L_RANGES[level].min, L_RANGES[level].mid + distance, L_RANGES[level].max);
|
||||
}
|
||||
|
||||
/**
|
||||
* How many tints are between two tints?
|
||||
* E.g. `getTintDistance('90', '95')` should return `1`
|
||||
* @param {number | string} tint1
|
||||
* @param {number | string} tint2
|
||||
* @returns {number}
|
||||
*/
|
||||
export function getTintDistance(tint1, tint2) {
|
||||
tint1 = String(tint1);
|
||||
tint2 = String(tint2);
|
||||
return tints.indexOf(tint2) - tints.indexOf(tint1);
|
||||
}
|
||||
export function getHueShift(color, fromTint, toTint) {
|
||||
let tintDistance = getTintDistance(fromTint, toTint);
|
||||
let hueShift = getRange(HUE_SHIFTS, color.get('oklch.h'), {
|
||||
getRange: v => v.range,
|
||||
type: 'angle',
|
||||
tolerance: 0,
|
||||
});
|
||||
|
||||
if (!hueShift) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
hueShift = HUE_SHIFTS[hueShift.key];
|
||||
|
||||
let { peak, range } = hueShift;
|
||||
let h = color.get('oklch.h');
|
||||
let breakpoints = [range[0], ...peak, range[1]];
|
||||
let intensity = mapRange(h, breakpoints, [0, 1, 1, 0]);
|
||||
let type = tintDistance < 0 ? 'dark' : 'light';
|
||||
let shift = hueShift.shift[type];
|
||||
|
||||
let ret = shift * intensity;
|
||||
let maxConsecutive = hueShift.maxConsecutive[type] ?? hueShift.maxConsecutive;
|
||||
let maxShift = Math.sign(shift) * maxConsecutive * Math.abs(tintDistance);
|
||||
|
||||
ret = clamp(undefined, ret, maxShift);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function getCoreTint(scale) {
|
||||
let tintsInScale = Object.keys(scale);
|
||||
|
||||
if (tintsInScale.length <= 1) {
|
||||
return tintsInScale[0];
|
||||
}
|
||||
|
||||
let ret = DEFAULT_ACCENT in scale ? DEFAULT_ACCENT : tintsInScale[Math.floor(tintsInScale.length / 2)];
|
||||
let maxChroma = 0;
|
||||
|
||||
for (let tint in scale) {
|
||||
let color = scale[tint];
|
||||
let chroma = color.get('oklch.c');
|
||||
|
||||
if (chroma > maxChroma + CHROMA_TOLERANCE && tint >= MIN_ACCENT && tint <= MAX_ACCENT) {
|
||||
ret = tint;
|
||||
maxChroma = chroma;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function getContrasts(colors, originalContrasts) {
|
||||
let ret = {};
|
||||
|
||||
for (let hue in colors) {
|
||||
ret[hue] = {};
|
||||
|
||||
for (let tintBg of tints) {
|
||||
ret[hue][tintBg] = {};
|
||||
let bgColor = colors[hue][tintBg];
|
||||
|
||||
if (!bgColor || !bgColor.contrast) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let tintFg of tints) {
|
||||
let fgColor = colors[hue][tintFg];
|
||||
let value = bgColor.contrast(fgColor, 'WCAG21');
|
||||
if (originalContrasts) {
|
||||
let original = originalContrasts[hue][tintBg][tintFg];
|
||||
ret[hue][tintBg][tintFg] = { value, original, bgColor, fgColor };
|
||||
} else {
|
||||
ret[hue][tintBg][tintFg] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return hex code iff a color is within sRGB, otherwise fall back to its default string representation
|
||||
*
|
||||
* @param {Color} color
|
||||
* @returns {string}
|
||||
*/
|
||||
export function stringifyColor(color) {
|
||||
if (color?.constructor.name !== 'Color') {
|
||||
return color;
|
||||
}
|
||||
|
||||
let format = color.inGamut('srgb') ? 'hex' : undefined;
|
||||
return color.toString({ format });
|
||||
}
|
||||
@@ -1,406 +0,0 @@
|
||||
/* CSS included both in predefined palettes and custom ones */
|
||||
:root {
|
||||
--fa-sliders-simple: '\f1de';
|
||||
}
|
||||
|
||||
.core-column {
|
||||
position: relative;
|
||||
|
||||
> wa-dropdown {
|
||||
min-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
wa-dropdown > .color.swatch {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.color-slider {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
|
||||
wa-slider {
|
||||
grid-column: 1 / -1;
|
||||
--track-height: 1em;
|
||||
--track-color-inactive: transparent;
|
||||
--track-color-active: transparent;
|
||||
--thumb-color: var(--color);
|
||||
--thumb-shadow: 0 0 0 var(--thumb-gap) var(--wa-color-surface-default),
|
||||
var(--wa-shadow-offset-x-m) var(--wa-shadow-offset-y-m) var(--wa-shadow-blur-m)
|
||||
calc(var(--wa-shadow-offset-x-m) * -1 + var(--thumb-gap)) var(--wa-color-shadow);
|
||||
|
||||
&:active {
|
||||
--thumb-size: 2em;
|
||||
}
|
||||
|
||||
&::part(base) {
|
||||
position: relative;
|
||||
background: linear-gradient(to right in var(--color-interpolation-space, oklab), var(--color-1), var(--color-2));
|
||||
}
|
||||
|
||||
.tick {
|
||||
--width: 1px;
|
||||
--height: 0.5em;
|
||||
--tick-color: var(--wa-color-neutral-border-normal);
|
||||
width: 4px;
|
||||
height: 2.4em;
|
||||
background: no-repeat;
|
||||
background-image: linear-gradient(var(--tick-color) 0 100%), linear-gradient(var(--tick-color) 0 100%);
|
||||
background-position: top, bottom;
|
||||
background-size: var(--width) var(--height);
|
||||
position: absolute;
|
||||
left: calc(var(--default-value-progress) * 100% - (var(--default-value-progress) - 0.5) * var(--thumb-size));
|
||||
translate: -50% 0;
|
||||
bottom: -0.5em;
|
||||
|
||||
&:hover {
|
||||
--tick-color: var(--wa-color-neutral-border-loud);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[slot='label'] {
|
||||
min-height: 1.1lh;
|
||||
}
|
||||
|
||||
.clear-button {
|
||||
vertical-align: middle;
|
||||
font-size: var(--wa-font-size-xs);
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
[data-component='h'] {
|
||||
--color-interpolation-space: oklch increasing hue;
|
||||
}
|
||||
|
||||
.popup {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
gap: var(--wa-space-m);
|
||||
background: var(--wa-color-surface-default);
|
||||
color: var(--wa-color-text-normal);
|
||||
border: 1px solid var(--wa-color-surface-border);
|
||||
padding: var(--wa-space-m) var(--wa-space-l);
|
||||
border-radius: var(--wa-border-radius-m);
|
||||
color-scheme: light;
|
||||
|
||||
.copyable-code {
|
||||
display: flex;
|
||||
gap: var(--wa-space-xs);
|
||||
align-items: center;
|
||||
|
||||
code {
|
||||
flex: 1;
|
||||
max-width: 20ch;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
> legend {
|
||||
/* Force legend to be rendered inside the fieldset */
|
||||
float: left;
|
||||
clear: all;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.wa-heading-s {
|
||||
display: flex;
|
||||
gap: var(--wa-gap-xs);
|
||||
align-items: center;
|
||||
|
||||
> :nth-child(1 of .align-end) {
|
||||
margin-inline-start: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@scope (.wa-dark) to (.wa-light) {
|
||||
.popup {
|
||||
color-scheme: dark;
|
||||
}
|
||||
}
|
||||
|
||||
.color-scale {
|
||||
th {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tweak-icon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
right: var(--wa-space-s);
|
||||
opacity: var(--tweak-icon-opacity, 0%);
|
||||
}
|
||||
|
||||
.copy-icon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%) translateY(-50%);
|
||||
opacity: var(--copy-icon-opacity, 0%);
|
||||
}
|
||||
|
||||
.color.swatch:hover {
|
||||
--tweak-icon-opacity: 40%;
|
||||
--copy-icon-opacity: 40%;
|
||||
}
|
||||
|
||||
wa-dropdown[open] .copy-icon {
|
||||
--copy-icon-opacity: 60%;
|
||||
}
|
||||
|
||||
&.tweaked .core-column {
|
||||
--tweak-icon-opacity: 80%;
|
||||
}
|
||||
}
|
||||
|
||||
.tweaked-callout {
|
||||
padding: var(--wa-space-xs);
|
||||
padding-inline-start: var(--wa-space-m);
|
||||
margin-block: var(--wa-space-m);
|
||||
align-items: center;
|
||||
|
||||
&:not(.tweaked-any *) {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
&::part(message) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--wa-space-xs);
|
||||
}
|
||||
|
||||
wa-button:first-of-type {
|
||||
margin-inline-start: auto;
|
||||
}
|
||||
}
|
||||
|
||||
/* Better UI before Vue initializes */
|
||||
[v-if='saved'],
|
||||
[v-if^='tweaked'],
|
||||
[v-cloak] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.static-palette:has(+ .colors:not([v-cloak])) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.core-color {
|
||||
wa-radio-button::part(base) {
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
padding: 0;
|
||||
border-radius: var(--wa-border-radius-circle);
|
||||
background: var(--color);
|
||||
background-clip: border-box;
|
||||
}
|
||||
|
||||
wa-radio-button:is([checked], :state(checked))::part(base) {
|
||||
box-shadow:
|
||||
inset 0 0 0 var(--indicator-width) var(--indicator-color),
|
||||
inset 0 0 0 calc(var(--indicator-width) + 1.5px) var(--wa-color-surface-default);
|
||||
}
|
||||
|
||||
&::part(form-control-input) {
|
||||
gap: var(--wa-space-xs);
|
||||
}
|
||||
}
|
||||
|
||||
[id='palette-info'] {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
grid-auto-flow: column;
|
||||
|
||||
> * {
|
||||
grid-column: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.hue-wheel {
|
||||
--r: clamp(2em, 6rem, 25vmin);
|
||||
grid-column: 2;
|
||||
grid-row: 1 / 5;
|
||||
position: relative;
|
||||
width: calc(var(--r) * 2);
|
||||
aspect-ratio: 1;
|
||||
border: 2px solid transparent;
|
||||
border-radius: 50%;
|
||||
--lc: var(--avg-l) var(--max-c);
|
||||
--lc2: var(--avg-l) calc(var(--max-c) / 2);
|
||||
margin-top: calc(var(--r) * -0.05);
|
||||
--cover-size: calc(100% - var(--visible-size, 0%));
|
||||
background:
|
||||
radial-gradient(closest-side, var(--wa-color-surface-default) 100%, transparent 0) center / var(--cover-size)
|
||||
var(--cover-size),
|
||||
conic-gradient(
|
||||
in oklch,
|
||||
oklch(var(--lc) 0),
|
||||
oklch(var(--lc) 60),
|
||||
oklch(var(--lc) 120),
|
||||
oklch(var(--lc) 180),
|
||||
oklch(var(--lc) 240),
|
||||
oklch(var(--lc) 300),
|
||||
oklch(var(--lc) 360)
|
||||
);
|
||||
background-origin: border-box;
|
||||
background-clip: padding-box, border-box;
|
||||
background-repeat: no-repeat;
|
||||
transition: var(--wa-transition-slow) ease-in background-size;
|
||||
|
||||
&:is(:hover, :focus-within) {
|
||||
--visible-size: 100%;
|
||||
}
|
||||
|
||||
&,
|
||||
&::before {
|
||||
--stops: oklch(var(--lc) 0), oklch(var(--lc) 60), oklch(var(--lc) 120), oklch(var(--lc) 180), oklch(var(--lc) 240),
|
||||
oklch(var(--lc) 300), oklch(var(--lc) 360);
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
display: block;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
mask: radial-gradient(white, transparent);
|
||||
mask-size: var(--visible-size, 0%) var(--visible-size, 0%);
|
||||
mask-repeat: no-repeat;
|
||||
transition: inherit;
|
||||
transition-property: mask-size;
|
||||
background: radial-gradient(oklch(var(--avg-l) calc(var(--gray-chroma) * var(--max-c)) 0) 5%, transparent 30%),
|
||||
conic-gradient(
|
||||
in oklch,
|
||||
oklch(var(--lc2) 0),
|
||||
oklch(var(--lc2) 60),
|
||||
oklch(var(--lc2) 120),
|
||||
oklch(var(--lc2) 180),
|
||||
oklch(var(--lc2) 240),
|
||||
oklch(var(--lc2) 300),
|
||||
oklch(var(--lc2) 360)
|
||||
);
|
||||
}
|
||||
|
||||
.color {
|
||||
--scale-c: calc(var(--c) / var(--max-c));
|
||||
--distance: calc(var(--r) * var(--scale-c));
|
||||
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%) rotate(calc(var(--h) * 1deg - 90deg)) translateX(var(--distance));
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
width: calc(1.2em + 0.3em * var(--scale-c));
|
||||
aspect-ratio: 1;
|
||||
|
||||
&:hover {
|
||||
--scale: 1.2;
|
||||
--line-color: white;
|
||||
--line-style: solid;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
width: 100%;
|
||||
height: 0;
|
||||
border-top: 2px var(--line-style, dashed) var(--line-color, var(--wa-color-gray-80));
|
||||
padding-top: 100%;
|
||||
top: calc(50% - 1px);
|
||||
right: 50%;
|
||||
width: var(--distance);
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
border: 2px solid white;
|
||||
box-shadow: var(--wa-shadow-l);
|
||||
background: var(--color);
|
||||
transition: var(--wa-transition-fast);
|
||||
scale: var(--scale, 1);
|
||||
}
|
||||
}
|
||||
|
||||
wa-tooltip {
|
||||
/* Prevent flickering */
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.scale-filter {
|
||||
wa-tab wa-icon {
|
||||
margin-right: 0.4em;
|
||||
}
|
||||
}
|
||||
|
||||
.title wa-icon-button[name='pencil'] {
|
||||
margin-inline-start: var(--wa-space-xs);
|
||||
}
|
||||
|
||||
.selected-swatch,
|
||||
.color-select wa-option::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 1.2em;
|
||||
aspect-ratio: 1;
|
||||
flex: none;
|
||||
border-radius: var(--wa-border-radius-m);
|
||||
background: var(--color);
|
||||
border: 1px solid var(--wa-color-surface-default);
|
||||
}
|
||||
|
||||
.color-select wa-option {
|
||||
white-space: nowrap;
|
||||
|
||||
&::before {
|
||||
width: 1em;
|
||||
margin-inline: var(--wa-space-xs);
|
||||
}
|
||||
|
||||
&::part(checked-icon) {
|
||||
order: 2;
|
||||
}
|
||||
|
||||
wa-icon[name='square-plus'] {
|
||||
vertical-align: -0.15em;
|
||||
color: var(--color-gray);
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
.color-popup {
|
||||
display: block;
|
||||
|
||||
.popup {
|
||||
min-width: 25ch;
|
||||
}
|
||||
}
|
||||
|
||||
wa-icon[name='thumbtack'],
|
||||
wa-icon-button[name='thumbtack']::part(icon) {
|
||||
rotate: 45deg;
|
||||
}
|
||||
@@ -1,554 +0,0 @@
|
||||
// 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 { createApp, nextTick } from 'https://cdn.jsdelivr.net/npm/vue@3/dist/vue.esm-browser.js';
|
||||
import generatePalette from './color/generate-palette.js';
|
||||
import getMaxChroma from './color/get-max-chroma.js';
|
||||
import getPaletteCode from './color/get-palette-code.js';
|
||||
import allPalettes from './color/palettes.js';
|
||||
import { tweakColor, tweakPalette } from './color/tweak.js';
|
||||
import { getContrasts, identifyColor } from './color/util.js';
|
||||
import ColorPopup from './vue-components/color-popup.js';
|
||||
import ColorSlider from './vue-components/color-slider.js';
|
||||
import ColorSwatchPicker from './vue-components/color-swatch-picker.js';
|
||||
import InfoTip from './vue-components/info-tip.js';
|
||||
import Prism from '/assets/scripts/prism.js';
|
||||
import { Permalink } from '/assets/scripts/tweak.js';
|
||||
import {
|
||||
allHues,
|
||||
cdnUrl,
|
||||
HUE_RANGES,
|
||||
hueAfter,
|
||||
hueBefore,
|
||||
hues,
|
||||
L_RANGES,
|
||||
MAX_CHROMA_BOUNDS,
|
||||
MAX_GRAY_CHROMA_SCALE,
|
||||
moreHue,
|
||||
ROLES,
|
||||
tints,
|
||||
} from '/assets/scripts/tweak/data.js';
|
||||
import { camelCase, capitalize, log, slugify, subtractAngles } from '/assets/scripts/tweak/util.js';
|
||||
|
||||
let paletteAppSpec = {
|
||||
data() {
|
||||
let appRoot = document.querySelector('#palette-app');
|
||||
let paletteId = appRoot.dataset.paletteId;
|
||||
let palette = allPalettes[paletteId];
|
||||
|
||||
return {
|
||||
uid: undefined,
|
||||
paletteId,
|
||||
originalPaletteTitle: palette.title,
|
||||
originalColors: palette.colors,
|
||||
baseColors: { ...palette.colors },
|
||||
permalink: new Permalink(),
|
||||
hueShifts: Object.fromEntries(hues.map(hue => [hue, 0])),
|
||||
chromaScale: 1,
|
||||
grayChroma: undefined,
|
||||
grayColor: undefined,
|
||||
saved: null,
|
||||
unsavedChanges: false,
|
||||
savedPalettes: sidebar.palettes.saved,
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
// Non-reactive variables to expose
|
||||
Object.assign(this, {
|
||||
moreHue,
|
||||
hueBefore,
|
||||
hueAfter,
|
||||
HUE_RANGES,
|
||||
L_RANGES,
|
||||
hues,
|
||||
allHues,
|
||||
tints,
|
||||
MAX_CHROMA_BOUNDS,
|
||||
});
|
||||
|
||||
if (location.search) {
|
||||
// Read URL params and apply them. This facilitates permalinks.
|
||||
for (let hue in this.hueShifts) {
|
||||
if (this.permalink.has(hue + '-shift')) {
|
||||
this.hueShifts[hue] = Number(this.permalink.get(hue + '-shift'));
|
||||
}
|
||||
}
|
||||
|
||||
for (let param of ['chroma-scale', 'gray-color', 'gray-chroma']) {
|
||||
if (this.permalink.has(param)) {
|
||||
let value = this.permalink.get(param);
|
||||
|
||||
if (!isNaN(value)) {
|
||||
// Convert numeric values to numbers
|
||||
value = Number(value);
|
||||
}
|
||||
|
||||
let prop = camelCase(param);
|
||||
this[prop] = value;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.permalink.has('uid')) {
|
||||
this.uid = Number(this.permalink.get('uid'));
|
||||
this.saved = sidebar.palettes.saved.find(p => p.uid === this.uid);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
nextTick().then(() => {
|
||||
if (!this.tweaked || this.saved) {
|
||||
this.unsavedChanges = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
computed: {
|
||||
/**
|
||||
* Stage of interaction with the palette app
|
||||
* 0: Static
|
||||
* 1: Started editing
|
||||
* 2: Edited
|
||||
* @returns
|
||||
*/
|
||||
step() {
|
||||
return this.tweaked ? 1 : 0;
|
||||
},
|
||||
|
||||
slug() {
|
||||
return this.paletteId;
|
||||
},
|
||||
|
||||
/** Default palette title for saving */
|
||||
defaultPaletteTitle() {
|
||||
return this.originalPaletteTitle + ' (tweaked)';
|
||||
},
|
||||
|
||||
paletteTitle() {
|
||||
if (this.step === 0) {
|
||||
return this.originalPaletteTitle;
|
||||
} else if (this.saved) {
|
||||
return this.saved.title;
|
||||
} else {
|
||||
return this.defaultPaletteTitle;
|
||||
}
|
||||
},
|
||||
|
||||
tweaks() {
|
||||
return {
|
||||
hueShifts: this.hueShifts,
|
||||
chromaScale: this.chromaScale,
|
||||
grayColor: this.grayColor,
|
||||
grayChroma: this.grayChroma,
|
||||
};
|
||||
},
|
||||
|
||||
code() {
|
||||
let ret = {};
|
||||
for (let language of ['html', 'css']) {
|
||||
let code = getPaletteCode({
|
||||
base: this.paletteId,
|
||||
colors: this.colors,
|
||||
tweaked: this.tweaked,
|
||||
language,
|
||||
cdnUrl,
|
||||
});
|
||||
ret[language] = {
|
||||
raw: code,
|
||||
highlighted: Prism.highlight(code, Prism.languages[language], language),
|
||||
};
|
||||
}
|
||||
|
||||
return ret;
|
||||
},
|
||||
|
||||
colors() {
|
||||
return tweakPalette.call(this, this.baseColors, this.tweaks, this.tweaked);
|
||||
},
|
||||
|
||||
tweaked() {
|
||||
let anyHueTweaked = Object.values(this.hueShifts).some(Boolean);
|
||||
let hue = anyHueTweaked
|
||||
? Object.fromEntries(Object.entries(this.hueShifts).map(([hue, shift]) => [hue, shift !== 0]))
|
||||
: false;
|
||||
|
||||
let ret = {
|
||||
chromaScale: this.chromaScale !== 1,
|
||||
hue,
|
||||
grayChroma: this.grayChroma !== undefined && this.grayChroma !== this.originalGrayChroma,
|
||||
grayColor: this.grayColor !== undefined && this.grayColor !== this.originalGrayColor,
|
||||
};
|
||||
|
||||
let anyTweaked = Object.values(ret).some(Boolean);
|
||||
return anyTweaked ? ret : false;
|
||||
},
|
||||
|
||||
tweaksHumanReadable() {
|
||||
let ret = {};
|
||||
|
||||
if (this.chromaScale !== 1) {
|
||||
ret.chromaScale = 'More ' + (this.chromaScale > 1 ? 'vibrant' : 'muted');
|
||||
}
|
||||
|
||||
for (let hue in this.hueShifts) {
|
||||
let shift = this.hueShifts[hue];
|
||||
|
||||
if (!shift) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let relHue = shift < 0 ? hueBefore[hue] : hueAfter[hue];
|
||||
let hueTweak = moreHue[relHue] ?? relHue + 'er';
|
||||
|
||||
ret[hue] = capitalize(hueTweak + ' ' + hue + 's');
|
||||
}
|
||||
|
||||
if (this.tweaked.grayChroma || this.tweaked.grayColor) {
|
||||
if (this.tweaked.grayChroma === 0) {
|
||||
ret.grayChroma = 'Achromatic grays';
|
||||
} else {
|
||||
if (this.tweaked.grayColor) {
|
||||
ret.grayColor = capitalize(this.grayColor) + ' gray undertone';
|
||||
}
|
||||
|
||||
if (this.tweaked.grayChroma) {
|
||||
let more = this.tweaked.grayChroma > this.originalGrayChroma;
|
||||
ret.grayChroma = `More ${more ? 'colorful' : 'neutral'} grays`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
},
|
||||
|
||||
originalContrasts() {
|
||||
return getContrasts(this.baseColors);
|
||||
},
|
||||
|
||||
contrasts() {
|
||||
return getContrasts(this.colors, this.originalContrasts);
|
||||
},
|
||||
|
||||
baseCoreColors() {
|
||||
let ret = {};
|
||||
for (let hue in this.baseColors) {
|
||||
ret[hue] = this.baseColors[hue].core;
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
|
||||
baseMaxChromaHue() {
|
||||
let maxChroma = -1;
|
||||
let maxChromaHue = null;
|
||||
|
||||
for (let hue in this.baseCoreColors) {
|
||||
let color = this.baseCoreColors[hue];
|
||||
let chroma = color.get('oklch.c');
|
||||
if (chroma > maxChroma || !maxChromaHue) {
|
||||
maxChroma = chroma;
|
||||
maxChromaHue = hue;
|
||||
}
|
||||
}
|
||||
return maxChromaHue;
|
||||
},
|
||||
|
||||
baseMaxChromaColor() {
|
||||
return this.baseCoreColors[this.baseMaxChromaHue];
|
||||
},
|
||||
|
||||
baseMaxChroma() {
|
||||
return this.baseMaxChromaColor.get('oklch.c');
|
||||
},
|
||||
|
||||
coreColors() {
|
||||
let ret = {};
|
||||
for (let hue in this.colors) {
|
||||
ret[hue] = this.colors[hue].core;
|
||||
}
|
||||
|
||||
return ret;
|
||||
},
|
||||
|
||||
maxChroma() {
|
||||
return Math.max(
|
||||
...Object.values(this.coreColors)
|
||||
.map(color => color.get('oklch.c'))
|
||||
.filter(c => c >= 0),
|
||||
);
|
||||
},
|
||||
|
||||
coreLevels() {
|
||||
let ret = {};
|
||||
|
||||
for (let hue in this.colors) {
|
||||
let maxChromaTint = this.colors[hue].maxChromaTint;
|
||||
ret[hue] = maxChromaTint;
|
||||
}
|
||||
|
||||
return ret;
|
||||
},
|
||||
|
||||
level() {
|
||||
let levels = Object.values(this.coreLevels).sort((a, b) => a - b);
|
||||
levels = levels.slice(levels.length / 4, -levels.length / 4); // Remove top and bottom 25%
|
||||
let trimmedMean = levels.map(Number).reduce((a, b) => a + b, 0) / levels.length;
|
||||
return Math.round(trimmedMean / 10) * 10;
|
||||
},
|
||||
|
||||
shiftBounds() {
|
||||
return Object.fromEntries(
|
||||
hues.map(hue => {
|
||||
let range = HUE_RANGES[hue];
|
||||
let coreHue = Math.round(this.baseCoreColors[hue].get('oklch.h'));
|
||||
return [hue, { min: range.min - coreHue, max: range.max - coreHue }];
|
||||
}),
|
||||
);
|
||||
},
|
||||
|
||||
chromaScaleBounds() {
|
||||
return { min: MAX_CHROMA_BOUNDS.min / this.baseMaxChroma, max: MAX_CHROMA_BOUNDS.max / this.baseMaxChroma };
|
||||
},
|
||||
|
||||
originalGrayColor() {
|
||||
let grayHue = this.baseCoreColors.gray.get('oklch.h');
|
||||
let minDistance = Infinity;
|
||||
let closestHue = null;
|
||||
|
||||
// Find core color whose hue is closest to our gray
|
||||
for (let name in this.baseCoreColors) {
|
||||
if (name === 'gray') {
|
||||
continue;
|
||||
}
|
||||
|
||||
let hue = this.baseCoreColors[name].get('oklch.h');
|
||||
let distance = Math.abs(subtractAngles(hue, grayHue));
|
||||
if (distance < minDistance) {
|
||||
minDistance = distance;
|
||||
closestHue = name;
|
||||
}
|
||||
}
|
||||
|
||||
return closestHue ?? 'indigo';
|
||||
},
|
||||
|
||||
originalGrayChroma() {
|
||||
let grayChroma = this.baseColors.gray.core.get('oklch.c');
|
||||
if (grayChroma === 0 || grayChroma === null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let grayColorChroma = this.baseColors[this.originalGrayColor].core.get('oklch.c');
|
||||
return grayChroma / grayColorChroma;
|
||||
},
|
||||
|
||||
/**
|
||||
* We want to preserve the original grayChroma selection so that when the user switches to another undertone
|
||||
* that supports higher chromas, their selection will be there.
|
||||
* This property is the gray chroma % that is actually applied.
|
||||
*/
|
||||
computedGrayChroma() {
|
||||
let grayChroma = this.grayChroma ?? this.originalGrayChroma;
|
||||
return Math.min(grayChroma, this.maxGrayChroma);
|
||||
},
|
||||
|
||||
computedGrayColor() {
|
||||
return this.grayColor ?? this.originalGrayColor;
|
||||
},
|
||||
|
||||
maxGrayChroma() {
|
||||
return MAX_GRAY_CHROMA_SCALE[this.grayColor] ?? 0.35;
|
||||
},
|
||||
|
||||
huesAfter() {
|
||||
let ret = {};
|
||||
let huesRotated = [...hues];
|
||||
for (let hue of hues) {
|
||||
let first = huesRotated.shift();
|
||||
ret[hue] = huesRotated.slice();
|
||||
huesRotated.push(first);
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
|
||||
/** Get other variants of the same base palette that are not this one */
|
||||
savedVariations() {
|
||||
return this.savedPalettes.filter(palette => palette.id === this.paletteId && palette.uid !== this.uid);
|
||||
},
|
||||
}, // end computed
|
||||
|
||||
watch: {
|
||||
hueShifts: {
|
||||
deep: true,
|
||||
handler() {
|
||||
for (let hue in this.hueShifts) {
|
||||
this.permalink.set(hue + '-shift', this.hueShifts[hue], 0);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
chromaScale() {
|
||||
this.permalink.set('chroma-scale', this.chromaScale, 1);
|
||||
},
|
||||
|
||||
grayColor() {
|
||||
this.permalink.set('gray-color', this.grayColor, this.originalGrayColor);
|
||||
},
|
||||
|
||||
grayChroma() {
|
||||
this.permalink.set('gray-chroma', this.grayChroma, this.originalGrayChroma);
|
||||
},
|
||||
|
||||
tweaks: {
|
||||
deep: true,
|
||||
async handler(value, oldValue) {
|
||||
await nextTick(); // must run after individual watchers
|
||||
|
||||
// Update page URL
|
||||
this.permalink.updateLocation();
|
||||
|
||||
this.unsavedChanges = true;
|
||||
},
|
||||
},
|
||||
|
||||
saved: {
|
||||
deep: true,
|
||||
handler() {
|
||||
this.unsavedChanges = !this.saved;
|
||||
},
|
||||
},
|
||||
}, // end watch
|
||||
|
||||
methods: {
|
||||
capitalize,
|
||||
slugify,
|
||||
getMaxChroma,
|
||||
log,
|
||||
|
||||
async save({ title } = {}) {
|
||||
let uid = this.uid;
|
||||
|
||||
this.saved ??= { id: this.paletteId, uid: this.uid };
|
||||
|
||||
if (title) {
|
||||
// Renaming
|
||||
this.saved.title = title;
|
||||
} else {
|
||||
this.saved.title ??= this.defaultPaletteTitle;
|
||||
}
|
||||
|
||||
this.saved.search = location.search;
|
||||
|
||||
this.saved = sidebar.palette.save(this.saved);
|
||||
|
||||
if (uid !== this.saved.uid) {
|
||||
// UID changed (most likely from saving a new palette)
|
||||
this.uid = this.saved.uid;
|
||||
this.permalink.set('uid', this.uid);
|
||||
this.permalink.updateLocation();
|
||||
await this.$nextTick();
|
||||
this.save(); // Save again to update the search param to include the UID
|
||||
}
|
||||
|
||||
this.unsavedChanges = false;
|
||||
},
|
||||
|
||||
rename() {
|
||||
let newTitle = prompt('Palette title:', this.saved?.title ?? this.defaultPaletteTitle);
|
||||
|
||||
if (newTitle && newTitle !== this.saved?.title) {
|
||||
this.save({ title: newTitle });
|
||||
}
|
||||
},
|
||||
|
||||
// Cannot name this delete() because Vue complains
|
||||
deleteSaved() {
|
||||
sidebar.palette.delete(this.saved);
|
||||
},
|
||||
|
||||
postDelete() {
|
||||
this.saved = null;
|
||||
this.permalink.delete('uid');
|
||||
this.uid = undefined;
|
||||
this.permalink.updateLocation();
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove a specific tweak or all tweaks
|
||||
* @param {string} [param] - The tweak to remove. If not provided, all tweaks are removed.
|
||||
*/
|
||||
reset(param, context = this) {
|
||||
if (!param || param === 'chromaScale') {
|
||||
context.chromaScale = 1;
|
||||
}
|
||||
|
||||
if (param in this.hueShifts) {
|
||||
context.hueShifts[param] = 0;
|
||||
} else if (!param) {
|
||||
for (let hue in this.hueShifts) {
|
||||
context.hueShifts[hue] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!param || param === 'grayColor') {
|
||||
context.grayColor = this.originalGrayColor;
|
||||
}
|
||||
|
||||
if (!param || param === 'grayChroma') {
|
||||
context.grayChroma = this.originalGrayChroma;
|
||||
}
|
||||
|
||||
return context;
|
||||
},
|
||||
}, // end methods
|
||||
|
||||
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;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
ColorPopup,
|
||||
ColorSlider,
|
||||
ColorSwatchPicker,
|
||||
InfoTip,
|
||||
},
|
||||
|
||||
compilerOptions: {
|
||||
isCustomElement: tag => tag.startsWith('wa-'),
|
||||
},
|
||||
};
|
||||
|
||||
function init() {
|
||||
let paletteAppContainer = document.querySelector('#palette-app');
|
||||
globalThis.paletteApp?.unmount?.();
|
||||
|
||||
if (!paletteAppContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
globalThis.paletteApp = createApp(paletteAppSpec).mount(paletteAppContainer);
|
||||
}
|
||||
|
||||
init();
|
||||
addEventListener('turbo:render', init);
|
||||
@@ -1,82 +0,0 @@
|
||||
import Color from 'https://colorjs.io/dist/color.js';
|
||||
import { stringifyColor } from '../color/util.js';
|
||||
import InfoTip from './info-tip.js';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
title: String,
|
||||
token: String,
|
||||
color: Color,
|
||||
deletable: Boolean,
|
||||
pinnable: Boolean,
|
||||
pinned: Boolean,
|
||||
placement: String,
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
emits: ['delete', 'pin'],
|
||||
mounted() {
|
||||
let popup = this.$refs.popup;
|
||||
|
||||
if (popup) {
|
||||
// Find trigger
|
||||
let trigger = popup.previousElementSibling;
|
||||
if (trigger) {
|
||||
trigger.slot ||= 'trigger';
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
stringifiedColor() {
|
||||
return stringifyColor(this.color);
|
||||
},
|
||||
},
|
||||
template: `
|
||||
<wa-dropdown class="color-popup" :placement>
|
||||
<slot></slot>
|
||||
<component :is="title ? 'fieldset' : 'div'" class="popup" ref="popup">
|
||||
<component :is="title ? 'legend' : 'div'" class="wa-heading-s" v-if="title || token || deletable || pinnable">
|
||||
<span v-if="title">{{ title }}</span>
|
||||
<wa-copy-button v-if="title && token" :value="token" :copy-label="token"></wa-copy-button>
|
||||
|
||||
<info-tip v-if="deletable && pinned">
|
||||
<wa-button size="small" variant="danger" appearance="plain" class="delete-button align-end" @click="$emit('delete')">
|
||||
<wa-icon name="trash" variant="regular"></wa-icon>
|
||||
</wa-button>
|
||||
<template #content>
|
||||
Delete from my colors
|
||||
</template>
|
||||
</info-tip>
|
||||
<info-tip v-if="pinnable && !pinned">
|
||||
<wa-button appearance="outlined" size="small" class="pin-color align-end" @click="$emit('pin')">
|
||||
<wa-icon name="thumbtack" variant="regular" slot="prefix"></wa-icon>
|
||||
Pin
|
||||
</wa-button>
|
||||
<template #content>
|
||||
Prevent this color from changing as others are edited
|
||||
</template>
|
||||
</info-tip>
|
||||
</component>
|
||||
|
||||
<slot name="content"></slot>
|
||||
|
||||
<div class="wa-stack wa-gap-xs">
|
||||
<div class="copyable-code" v-if="token && !title">
|
||||
<code>{{ token }}</code>
|
||||
<wa-copy-button :value="token"></wa-copy-button>
|
||||
</div>
|
||||
<div class="copyable-code" v-if="color">
|
||||
<code>{{ stringifiedColor }}</code>
|
||||
<wa-copy-button :value="stringifiedColor"></wa-copy-button>
|
||||
</div>
|
||||
</div>
|
||||
</component>
|
||||
</wa-dropdown>`,
|
||||
compilerOptions: {
|
||||
isCustomElement: tag => tag.startsWith('wa-'),
|
||||
},
|
||||
components: {
|
||||
InfoTip,
|
||||
},
|
||||
};
|
||||
@@ -1,73 +0,0 @@
|
||||
import { capitalize } from '/assets/scripts/tweak/util.js';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
modelValue: String,
|
||||
label: String,
|
||||
getLabel: {
|
||||
type: Function,
|
||||
default: capitalize,
|
||||
},
|
||||
getContent: {
|
||||
type: Function,
|
||||
},
|
||||
getColor: {
|
||||
type: Function,
|
||||
default: value => `var(--wa-color-${value})`,
|
||||
},
|
||||
values: {
|
||||
type: Array,
|
||||
default: [],
|
||||
},
|
||||
groups: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue', 'input'],
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
computed: {
|
||||
computedGroups() {
|
||||
let ret = {};
|
||||
|
||||
if (this.values?.length) {
|
||||
ret[''] = this.values;
|
||||
}
|
||||
|
||||
if (this.groups) {
|
||||
for (let group in this.groups) {
|
||||
if (this.groups[group]?.length) {
|
||||
ret[group] = this.groups[group];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
},
|
||||
|
||||
firstGroup() {
|
||||
return Object.keys(this.computedGroups)[0];
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
capitalize,
|
||||
handleInput(e) {
|
||||
this.$emit('input', this.modelValue);
|
||||
},
|
||||
},
|
||||
template: `
|
||||
<wa-select class="color-select" name="brand" :label="label" :value="modelValue" @input="$emit('update:modelValue', $event.target.value)"
|
||||
:style="{'--color': getColor(modelValue)}">
|
||||
<template v-for="values, group in computedGroups">
|
||||
<template v-if="group">
|
||||
<wa-divider v-if="group !== firstGroup"></wa-divider>
|
||||
<small>{{ group }}</small>
|
||||
</template>
|
||||
<wa-option v-if="values?.length" v-for="value of values" :label="getLabel(value)" :value="value" :style="{'--color': getColor(value)}" v-html="getContent?.(value) ?? getLabel(value)"></wa-option>
|
||||
</template>
|
||||
<slot></slot>
|
||||
</wa-select>
|
||||
`,
|
||||
};
|
||||
@@ -1,343 +0,0 @@
|
||||
const template = `
|
||||
<div class="color-slider" :style="{
|
||||
'--color': computedColor, '--color-1': colorMin, '--color-2': colorMax,
|
||||
'--default-value-progress': defaultProgress,
|
||||
}" :data-component="coord || null">
|
||||
<wa-slider ref="slider" :min="computedMin" :max="computedMax" :step="step" :value="value"
|
||||
@input="handleInput($event.target.value);" @change="inputEnd($event.target.value)">
|
||||
<div slot="label">
|
||||
{{ label }}
|
||||
<wa-icon-button v-if="value !== computedDefaultValue" @click="reset" class="clear-button" name="circle-xmark" library="system" variant="regular" label="Reset"></wa-icon-button>
|
||||
<info-tip>
|
||||
<div class="tick"></div>
|
||||
<template #content>{{ computedLabelDefault }}</template>
|
||||
</info-tip>
|
||||
</div>
|
||||
</wa-slider>
|
||||
<div class="label-min">{{ labelMin }}</div>
|
||||
<div class="label-max">{{ labelMax }}</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
import Color from 'https://colorjs.io/dist/color.js';
|
||||
import InfoTip from './info-tip.js';
|
||||
import { capitalize, promise, roundTo } from '/assets/scripts/tweak/util.js';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
coord: {
|
||||
type: String,
|
||||
required: true,
|
||||
validator(value) {
|
||||
return ['l', 'c', 'h'].includes(value);
|
||||
},
|
||||
},
|
||||
color: Color,
|
||||
defaultColor: Color,
|
||||
|
||||
defaultValue: Number,
|
||||
defaultValueRelative: Number,
|
||||
|
||||
/** Used for relative types. Defaults to defaultValue if not provided. */
|
||||
baseValue: Number,
|
||||
|
||||
/** Used for formatting only. Only specify if different from base value. */
|
||||
formatBaseValue: {
|
||||
type: Number,
|
||||
default: undefined,
|
||||
},
|
||||
|
||||
modelValue: {
|
||||
type: Number,
|
||||
},
|
||||
min: Number,
|
||||
max: Number,
|
||||
minRelative: Number,
|
||||
maxRelative: Number,
|
||||
step: {
|
||||
type: Number,
|
||||
default: 0.001,
|
||||
},
|
||||
|
||||
type: {
|
||||
type: String,
|
||||
default: 'raw',
|
||||
},
|
||||
formatType: {
|
||||
type: String,
|
||||
},
|
||||
|
||||
label: String,
|
||||
labelMin: String,
|
||||
labelMax: String,
|
||||
labelDefault: String,
|
||||
},
|
||||
emits: ['update:modelValue', 'update:color', 'input'],
|
||||
data() {
|
||||
return {
|
||||
mounted: promise(),
|
||||
initialColor: this.color,
|
||||
value: undefined,
|
||||
tweaking: false,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
if (!this.color && !this.defaultColor) {
|
||||
console.warn(
|
||||
`[${this.label}]`,
|
||||
'<color-slider> requires at least one of the following props: color, defaultColor',
|
||||
);
|
||||
}
|
||||
|
||||
if (this.modelValue !== undefined) {
|
||||
this.value = this.getAbsoluteValue(this.modelValue);
|
||||
} else if (this.color) {
|
||||
this.value = this.colorCoords[this.coordIndex];
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.$refs.slider) {
|
||||
this.$refs.slider.tooltipFormatter = value => this.formatValue(value);
|
||||
this.$refs.slider.colorSliderData = this; // for debugging
|
||||
}
|
||||
|
||||
this.mounted.resolve();
|
||||
},
|
||||
beforeUnmount() {
|
||||
delete this.$refs.slider?.colorSliderData;
|
||||
},
|
||||
computed: {
|
||||
computedMin() {
|
||||
if (this.minRelative !== undefined) {
|
||||
return getAbsoluteValue(this.minRelative);
|
||||
}
|
||||
|
||||
return this.min;
|
||||
},
|
||||
|
||||
computedMax() {
|
||||
if (this.maxRelative !== undefined) {
|
||||
return this.getAbsoluteValue(this.maxRelative);
|
||||
}
|
||||
|
||||
return this.max;
|
||||
},
|
||||
|
||||
computedColor() {
|
||||
return this.getColorAt(this.value);
|
||||
},
|
||||
|
||||
computedColorCoords() {
|
||||
return this.computedColor.oklch.slice();
|
||||
},
|
||||
|
||||
colorCoords() {
|
||||
let color = this.color ?? this.computedColor;
|
||||
return color?.oklch.slice();
|
||||
},
|
||||
|
||||
computedColorString() {
|
||||
return `oklch(${this.computedColorCoords.join(' ')})`;
|
||||
},
|
||||
|
||||
colorString() {
|
||||
return `oklch(${this.colorCoords.join(' ')})`;
|
||||
},
|
||||
|
||||
defaultCoords() {
|
||||
if (this.defaultColor) {
|
||||
return this.defaultColor.oklch.slice();
|
||||
}
|
||||
|
||||
let ret = this.color.oklch.slice();
|
||||
|
||||
if (this.defaultValue !== undefined) {
|
||||
ret[this.coordIndex] = this.defaultValue;
|
||||
}
|
||||
|
||||
return ret;
|
||||
},
|
||||
|
||||
coordIndex() {
|
||||
return ['l', 'c', 'h'].indexOf(this.coord);
|
||||
},
|
||||
|
||||
colorMin() {
|
||||
return this.getColorAt(this.computedMin);
|
||||
},
|
||||
|
||||
colorMax() {
|
||||
return this.getColorAt(this.computedMax);
|
||||
},
|
||||
|
||||
isRelative() {
|
||||
return this.type && this.type !== 'raw';
|
||||
},
|
||||
|
||||
computedBaseValue() {
|
||||
if (!this.isRelative) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.baseValue !== undefined) {
|
||||
return this.baseValue;
|
||||
}
|
||||
|
||||
return this.computedDefaultValue;
|
||||
},
|
||||
|
||||
computedDefaultValue() {
|
||||
if (this.defaultValue !== undefined) {
|
||||
return this.defaultValue;
|
||||
}
|
||||
|
||||
if (this.defaultValueRelative !== undefined) {
|
||||
return this.getAbsoluteValue(this.defaultValueRelative);
|
||||
}
|
||||
|
||||
if (this.baseValue !== undefined) {
|
||||
return this.baseValue;
|
||||
}
|
||||
|
||||
return this.defaultCoords[this.coordIndex];
|
||||
},
|
||||
|
||||
computedDefaultColor() {
|
||||
return this.defaultColor ?? this.getColorAt(this.computedDefaultValue);
|
||||
},
|
||||
|
||||
computedLabelDefault() {
|
||||
let labelDefault = this.labelDefault || 'Default value';
|
||||
let formattedDefaultValue = this.formatValue(this.computedDefaultValue);
|
||||
return `${labelDefault} (${formattedDefaultValue})`;
|
||||
},
|
||||
|
||||
defaultProgress() {
|
||||
return (this.computedDefaultValue - this.computedMin) / (this.computedMax - this.computedMin);
|
||||
},
|
||||
|
||||
relativeValue() {
|
||||
this.computedBaseValue;
|
||||
return this.getRelativeValue(this.value);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
capitalize,
|
||||
|
||||
getAbsoluteValue(relativeValue) {
|
||||
return getAbsoluteValue({
|
||||
type: this.type,
|
||||
relativeValue,
|
||||
baseValue: this.baseValue ?? this.computedBaseValue,
|
||||
});
|
||||
},
|
||||
|
||||
getRelativeValue(absoluteValue) {
|
||||
return getRelativeValue({
|
||||
type: this.type,
|
||||
absoluteValue,
|
||||
baseValue: this.baseValue ?? this.computedBaseValue,
|
||||
});
|
||||
},
|
||||
|
||||
formatValue(value = this.value) {
|
||||
let formatType = this.formatType ?? this.type;
|
||||
let style = formatType === 'scale' ? 'percent' : undefined;
|
||||
|
||||
if (formatType && formatType !== 'raw') {
|
||||
let baseValue = this.formatBaseValue ?? this.computedBaseValue;
|
||||
value = getRelativeValue({ type: formatType, absoluteValue: value, baseValue });
|
||||
}
|
||||
|
||||
value = roundTo(value, this.step);
|
||||
return value.toLocaleString(undefined, { style });
|
||||
},
|
||||
|
||||
getColorAt(value) {
|
||||
let coords = this.defaultCoords.slice();
|
||||
coords[this.coordIndex] = value;
|
||||
return new Color('oklch', coords);
|
||||
},
|
||||
|
||||
/** Called when value changes due to user interaction */
|
||||
handleInput(value) {
|
||||
this.value = value;
|
||||
this.tweaking = true;
|
||||
this.$emit('input', value);
|
||||
},
|
||||
|
||||
inputEnd() {
|
||||
this.tweaking = false;
|
||||
},
|
||||
|
||||
reset() {
|
||||
this.handleInput(this.computedDefaultValue);
|
||||
this.inputEnd();
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
computedColorString() {
|
||||
if (this.color && this.colorString !== this.computedColorString) {
|
||||
// Color changed, communicate to the outside world
|
||||
this.$emit('update:color', this.computedColor);
|
||||
}
|
||||
},
|
||||
|
||||
colorString() {
|
||||
if (this.color && this.colorString !== this.computedColorString) {
|
||||
// Color changed in the outside world, update our internals
|
||||
if (this.colorCoords[this.coordIndex] !== this.value) {
|
||||
this.value = this.colorCoords[this.coordIndex];
|
||||
|
||||
let modelValue = this.getRelativeValue(this.value);
|
||||
this.$emit('update:modelValue', modelValue);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
relativeValue() {
|
||||
this.$emit('update:modelValue', this.relativeValue);
|
||||
},
|
||||
},
|
||||
template,
|
||||
components: { InfoTip },
|
||||
compilerOptions: {
|
||||
isCustomElement: tag => tag.startsWith('wa-'),
|
||||
},
|
||||
};
|
||||
|
||||
function getAbsoluteValue({ type, relativeValue, baseValue }) {
|
||||
if (baseValue === undefined) {
|
||||
type = 'raw';
|
||||
}
|
||||
|
||||
if (type === 'shift') {
|
||||
return relativeValue + baseValue;
|
||||
}
|
||||
|
||||
if (type === 'scale') {
|
||||
return relativeValue * baseValue;
|
||||
}
|
||||
|
||||
return relativeValue;
|
||||
}
|
||||
|
||||
function getRelativeValue({ type, absoluteValue, baseValue }) {
|
||||
if (baseValue === undefined) {
|
||||
type = 'raw';
|
||||
}
|
||||
|
||||
if (type === 'shift') {
|
||||
return absoluteValue - baseValue;
|
||||
}
|
||||
|
||||
if (type === 'scale') {
|
||||
if (!absoluteValue) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return absoluteValue / baseValue;
|
||||
}
|
||||
|
||||
return absoluteValue;
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
const template = `
|
||||
<wa-radio-group class="core-color" orientation="horizontal" :value="modelValue" @input="handleInput($event.target.value)">
|
||||
<template v-for="h in hues">
|
||||
<info-tip>
|
||||
<wa-radio-button :label="capitalize(h)" :value="h" :style="{'--color': colors[h]}"></wa-radio-button>
|
||||
<template #content>{{ capitalize(h) }}</template>
|
||||
</info-tip>
|
||||
</template>
|
||||
<div slot="label">{{ label }}</div>
|
||||
</wa-radio-group>
|
||||
`;
|
||||
|
||||
import Color from 'https://colorjs.io/dist/color.js';
|
||||
import InfoTip from './info-tip.js';
|
||||
import { hues } from '/assets/scripts/tweak/data.js';
|
||||
import { capitalize, promise, roundTo } from '/assets/scripts/tweak/util.js';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
modelValue: String,
|
||||
label: {
|
||||
type: String,
|
||||
default: 'Color',
|
||||
},
|
||||
colors: Object,
|
||||
},
|
||||
emits: ['update:modelValue', 'input'],
|
||||
data() {
|
||||
return {
|
||||
defaultValue: this.modelValue,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
Object.assign(this, { hues });
|
||||
},
|
||||
computed: {},
|
||||
methods: {
|
||||
capitalize,
|
||||
|
||||
/** Called when value changes due to user interaction */
|
||||
handleInput(value) {
|
||||
this.value = value;
|
||||
this.$emit('input', value);
|
||||
this.$emit('update:modelValue', value);
|
||||
},
|
||||
|
||||
reset() {
|
||||
this.handleInput(this.defaultValue);
|
||||
},
|
||||
},
|
||||
template,
|
||||
components: { InfoTip },
|
||||
compilerOptions: {
|
||||
isCustomElement: tag => tag.startsWith('wa-'),
|
||||
},
|
||||
};
|
||||
@@ -1,37 +0,0 @@
|
||||
import Color from 'https://colorjs.io/dist/color.js';
|
||||
import { stringifyColor } from '../color/util.js';
|
||||
|
||||
let maxUid = 0;
|
||||
|
||||
export default {
|
||||
props: {},
|
||||
data() {
|
||||
let uid = ++maxUid;
|
||||
return { uid, id: 'info-tip-' + uid };
|
||||
},
|
||||
mounted() {
|
||||
let tooltip = this.$refs.tooltip;
|
||||
if (tooltip) {
|
||||
// Find trigger
|
||||
let trigger = tooltip.previousElementSibling;
|
||||
if (trigger) {
|
||||
if (trigger.id) {
|
||||
// Already has id
|
||||
this.id = trigger.id;
|
||||
} else {
|
||||
trigger.id = this.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
template: `
|
||||
<slot>
|
||||
<wa-icon-button :id="id" name="circle-question" variant="regular"></wa-icon-button>
|
||||
</slot>
|
||||
<wa-tooltip :for="id" ref="tooltip"><slot name="content"></slot></wa-tooltip>
|
||||
`,
|
||||
compilerOptions: {
|
||||
isCustomElement: tag => tag.startsWith('wa-'),
|
||||
},
|
||||
};
|
||||
@@ -5,6 +5,6 @@ layout: overview
|
||||
override:tags: []
|
||||
forTag: palette
|
||||
categories:
|
||||
tags: [other, pro]
|
||||
other: Free
|
||||
pro: Pro
|
||||
---
|
||||
|
||||
205
docs/docs/palettes/tweak.css
Normal file
205
docs/docs/palettes/tweak.css
Normal file
@@ -0,0 +1,205 @@
|
||||
:root {
|
||||
--fa-sliders-simple: '\f1de';
|
||||
}
|
||||
|
||||
.core-column {
|
||||
position: relative;
|
||||
|
||||
> wa-dropdown {
|
||||
min-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
wa-dropdown > .color.swatch {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.decorated-slider {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
margin-block-end: var(--wa-space-xl);
|
||||
|
||||
wa-slider {
|
||||
grid-column: 1 / -1;
|
||||
--track-height: 1em;
|
||||
--track-color-inactive: transparent;
|
||||
--track-color-active: transparent;
|
||||
--thumb-color: var(--color-tweaked, var(--color));
|
||||
--thumb-shadow: 0 0 0 var(--thumb-gap) var(--wa-color-surface-default),
|
||||
var(--wa-shadow-offset-x-m) var(--wa-shadow-offset-y-m) var(--wa-shadow-blur-m)
|
||||
calc(var(--wa-shadow-offset-x-m) * -1 + var(--thumb-gap)) var(--wa-color-shadow);
|
||||
|
||||
&:active {
|
||||
--thumb-size: 2em;
|
||||
}
|
||||
|
||||
&::part(base) {
|
||||
background: linear-gradient(to right in var(--color-interpolation-space, oklab), var(--color-1), var(--color-2));
|
||||
}
|
||||
}
|
||||
|
||||
[slot='label'] {
|
||||
min-height: 1.1lh;
|
||||
}
|
||||
|
||||
.clear-button {
|
||||
vertical-align: middle;
|
||||
font-size: var(--wa-font-size-xs);
|
||||
|
||||
&:not(.tweaked *) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.label-min,
|
||||
.label-max {
|
||||
font-size: var(--wa-font-size-xs);
|
||||
}
|
||||
|
||||
.label-min {
|
||||
grid-column: 1;
|
||||
margin-inline-start: 0.15em;
|
||||
}
|
||||
|
||||
.label-max {
|
||||
grid-column: 3;
|
||||
margin-inline-end: 0.1em;
|
||||
}
|
||||
}
|
||||
|
||||
.hue-shift-slider {
|
||||
--color-1: oklch(from var(--color) l c calc(h + var(--min, 0)));
|
||||
--color-2: oklch(from var(--color) l c calc(h + var(--max, 0)));
|
||||
--color-interpolation-space: oklch;
|
||||
}
|
||||
|
||||
.chroma-scale-slider {
|
||||
--color: var(--wa-color-brand);
|
||||
--color-1: oklch(from var(--color) l calc(c * var(--min)) h);
|
||||
--color-2: oklch(from var(--color) l calc(c * var(--max)) h);
|
||||
}
|
||||
|
||||
.gray-chroma-slider {
|
||||
--color: var(--wa-color-gray);
|
||||
--color-1: oklch(from var(--wa-color-gray) l 0 none);
|
||||
--color-2: oklch(from var(--color-gray-undertone) l calc(c * var(--max)) h);
|
||||
margin-top: var(--wa-space-m);
|
||||
}
|
||||
|
||||
.popup {
|
||||
background: var(--wa-color-surface-default);
|
||||
border: 1px solid var(--wa-color-surface-border);
|
||||
padding: var(--wa-space-xl);
|
||||
border-radius: var(--wa-border-radius-m);
|
||||
|
||||
code {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.color-scale {
|
||||
th {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
td:not([data-hue='gray'] *) {
|
||||
--tweak-c: calc(c * var(--chroma-scale, 1));
|
||||
--tweak-h: calc(h + var(--hue-shift, 0));
|
||||
|
||||
--color-tweaked-no-chroma-scale: oklch(from var(--color) l c var(--tweak-h));
|
||||
--color-tweaked-no-hue-shift: oklch(from var(--color) l var(--tweak-c) h);
|
||||
|
||||
&:is([data-tint='90'], [data-tint='95']) {
|
||||
/* Work around https://bugs.webkit.org/show_bug.cgi?id=287637 */
|
||||
|
||||
--color-tweaked-no-chroma-scale: lch(from var(--color) l c var(--tweak-h));
|
||||
--color-tweaked-no-hue-shift: lch(from var(--color) l var(--tweak-c) h);
|
||||
|
||||
/* outline: 1px dashed red; */
|
||||
}
|
||||
}
|
||||
|
||||
.color.swatch {
|
||||
--color-2: var(--color-tweaked);
|
||||
--color-2-height: 100%;
|
||||
|
||||
&:is(.tweaking *) {
|
||||
--color-2-height: 70%;
|
||||
}
|
||||
|
||||
&:is(.tweaking-chroma *) {
|
||||
--color: var(--color-tweaked-no-chroma-scale);
|
||||
}
|
||||
|
||||
&:is(.tweaking-hue *) {
|
||||
--color: var(--color-tweaked-no-hue-shift);
|
||||
}
|
||||
|
||||
&:is(.tweaking-gray-chroma *) {
|
||||
--color: var(--color-tweaked-no-gray-chroma);
|
||||
}
|
||||
}
|
||||
|
||||
.tweak-icon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
right: var(--wa-space-s);
|
||||
opacity: var(--tweak-icon-opacity, 0%);
|
||||
}
|
||||
|
||||
.core-column:hover {
|
||||
--tweak-icon-opacity: 40%;
|
||||
}
|
||||
|
||||
&.tweaked .core-column {
|
||||
--tweak-icon-opacity: 80%;
|
||||
}
|
||||
}
|
||||
|
||||
.tweaked-callout {
|
||||
padding: var(--wa-space-xs);
|
||||
padding-inline-start: var(--wa-space-m);
|
||||
align-items: center;
|
||||
|
||||
&:not(.tweaked-any *) {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
&::part(message) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--wa-space-xs);
|
||||
}
|
||||
|
||||
wa-button:first-of-type {
|
||||
margin-inline-start: auto;
|
||||
}
|
||||
}
|
||||
|
||||
/* Better UI before Vue initializes */
|
||||
[v-if='saved'],
|
||||
[v-if^='tweaked'] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.core-color {
|
||||
wa-radio-button::part(base) {
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
padding: 0;
|
||||
border-radius: var(--wa-border-radius-circle);
|
||||
background: var(--color);
|
||||
background-clip: border-box;
|
||||
}
|
||||
|
||||
wa-radio-button:is([checked], :state(checked))::part(base) {
|
||||
box-shadow:
|
||||
inset 0 0 0 var(--indicator-width) var(--indicator-color),
|
||||
inset 0 0 0 calc(var(--indicator-width) + 1.5px) var(--wa-color-surface-default);
|
||||
}
|
||||
|
||||
&::part(form-control-input) {
|
||||
gap: var(--wa-space-xs);
|
||||
}
|
||||
}
|
||||
580
docs/docs/palettes/tweak.js
Normal file
580
docs/docs/palettes/tweak.js
Normal file
@@ -0,0 +1,580 @@
|
||||
// TODO move these to local imports
|
||||
import Color from 'https://colorjs.io/dist/color.js';
|
||||
import { createApp, nextTick } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js';
|
||||
import { cdnUrl, hueRanges, hues, Permalink, tints } from '../../assets/scripts/tweak.js';
|
||||
import { cssImport, cssLiteral, cssRule } from '../../assets/scripts/tweak/code.js';
|
||||
import { maxGrayChroma, moreHue, selectors, urls } from '../../assets/scripts/tweak/data.js';
|
||||
import { subtractAngles } from '../../assets/scripts/tweak/util.js';
|
||||
import Prism from '/assets/scripts/prism.js';
|
||||
|
||||
await Promise.all(['wa-slider'].map(tag => customElements.whenDefined(tag)));
|
||||
|
||||
// // Detect https://bugs.webkit.org/show_bug.cgi?id=287637
|
||||
// const SAFARI_OKLCH_BUG = (() => {
|
||||
// let dummy = document.createElement('div');
|
||||
// document.body.appendChild(dummy);
|
||||
// dummy.style.color = 'oklch(from #d5e0e6 l c h)';
|
||||
// let computedColor = getComputedStyle(dummy).color;
|
||||
// dummy.remove();
|
||||
// return computedColor.endsWith(' 0)');
|
||||
// })();
|
||||
|
||||
let allPalettes = await fetch('/docs/palettes/data.json').then(r => r.json());
|
||||
globalThis.allPalettes = allPalettes;
|
||||
|
||||
for (let palette in allPalettes) {
|
||||
for (let hue in allPalettes[palette].colors) {
|
||||
let scale = allPalettes[palette].colors[hue];
|
||||
for (let tint of tints) {
|
||||
let color = scale[tint];
|
||||
|
||||
if (Array.isArray(color)) {
|
||||
scale[tint] = new Color('oklch', color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const percentFormatter = value => value.toLocaleString(undefined, { style: 'percent' });
|
||||
|
||||
let paletteAppSpec = {
|
||||
data() {
|
||||
let appRoot = document.querySelector('#palette-app');
|
||||
let paletteId = appRoot.dataset.paletteId;
|
||||
let palette = allPalettes[paletteId];
|
||||
|
||||
return {
|
||||
uid: undefined,
|
||||
paletteId,
|
||||
paletteTitle: palette.title,
|
||||
originalColors: palette.colors,
|
||||
permalink: new Permalink(),
|
||||
hueRanges,
|
||||
hueShifts: Object.fromEntries(hues.map(hue => [hue, 0])),
|
||||
chromaScale: 1,
|
||||
grayChroma: undefined,
|
||||
grayColor: undefined,
|
||||
tweaking: {},
|
||||
saved: null,
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
// Non-reactive variables to expose
|
||||
Object.assign(this, { moreHue });
|
||||
|
||||
// Read URL params and apply them. This facilitates permalinks.
|
||||
this.permalink.mapObject(this.hueShifts, {
|
||||
keyTo: key => key.replace(/-shift$/, ''),
|
||||
keyFrom: key => key + '-shift',
|
||||
valueFrom: value => (!value ? '' : Number(value)),
|
||||
valueTo: value => (!value ? 0 : Number(value)),
|
||||
});
|
||||
|
||||
this.grayChroma = this.originalGrayChroma;
|
||||
this.grayColor = this.originalGrayColor;
|
||||
|
||||
if (location.search) {
|
||||
// Update from URL
|
||||
this.permalink.writeTo(this.hueShifts);
|
||||
|
||||
for (let param of ['chroma-scale', 'gray-color', 'gray-chroma']) {
|
||||
if (this.permalink.has(param)) {
|
||||
let value = this.permalink.get(param);
|
||||
|
||||
if (!isNaN(value)) {
|
||||
// Convert numeric values to numbers
|
||||
value = Number(value);
|
||||
}
|
||||
|
||||
let prop = camelCase(param);
|
||||
this[prop] = value;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.permalink.has('uid')) {
|
||||
this.uid = Number(this.permalink.get('uid'));
|
||||
}
|
||||
|
||||
this.saved = sidebar.palette.getSaved(this.getPalette());
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
for (let ref in this.$refs) {
|
||||
this.$refs[ref].tooltipFormatter = percentFormatter;
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
tweaks() {
|
||||
return {
|
||||
hueShifts: this.hueShifts,
|
||||
chromaScale: this.chromaScale,
|
||||
grayColor: this.grayColor,
|
||||
grayChroma: this.grayChroma,
|
||||
};
|
||||
},
|
||||
|
||||
isTweaked() {
|
||||
return Object.values(this.hueShifts).some(Boolean);
|
||||
},
|
||||
|
||||
code() {
|
||||
let ret = {};
|
||||
for (let language of ['html', 'css']) {
|
||||
let code = getPaletteCode(this.paletteId, this.colors, this.tweaked, { language, cdnUrl });
|
||||
ret[language] = {
|
||||
raw: code,
|
||||
highlighted: Prism.highlight(code, Prism.languages[language], language),
|
||||
};
|
||||
}
|
||||
|
||||
return ret;
|
||||
},
|
||||
|
||||
colors() {
|
||||
return applyTweaks.call(this, this.originalColors, this.tweaks, this.tweaked);
|
||||
},
|
||||
|
||||
colorsMinusChromaScale() {
|
||||
let tweaked = { ...this.tweaked, chromaScale: false };
|
||||
return applyTweaks.call(this, this.originalColors, this.tweaks, tweaked);
|
||||
},
|
||||
|
||||
colorsMinusHueShifts() {
|
||||
let tweaked = { ...this.tweaked, hue: false };
|
||||
return applyTweaks.call(this, this.originalColors, this.tweaks, tweaked);
|
||||
},
|
||||
|
||||
colorsMinusGrayChroma() {
|
||||
let tweaked = { ...this.tweaked, grayChroma: false };
|
||||
return applyTweaks.call(this, this.originalColors, this.tweaks, tweaked);
|
||||
},
|
||||
|
||||
tweaked() {
|
||||
let anyHueTweaked = Object.values(this.hueShifts).some(Boolean);
|
||||
let hue = anyHueTweaked
|
||||
? Object.fromEntries(Object.entries(this.hueShifts).map(([hue, shift]) => [hue, shift !== 0]))
|
||||
: false;
|
||||
|
||||
let ret = {
|
||||
chromaScale: this.chromaScale !== 1,
|
||||
hue,
|
||||
grayChroma: this.grayChroma !== this.originalGrayChroma,
|
||||
grayColor: this.grayColor !== this.originalGrayColor,
|
||||
};
|
||||
|
||||
let anyTweaked = Object.values(ret).some(Boolean);
|
||||
return anyTweaked ? ret : false;
|
||||
},
|
||||
|
||||
tweaksHumanReadable() {
|
||||
let ret = {};
|
||||
|
||||
if (this.chromaScale !== 1) {
|
||||
ret.chromaScale = 'More ' + (this.chromaScale > 1 ? 'vibrant' : 'muted');
|
||||
}
|
||||
|
||||
for (let hue in this.hueShifts) {
|
||||
let shift = this.hueShifts[hue];
|
||||
|
||||
if (!shift) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let relHue = shift < 0 ? arrayPrevious(hues, hue) : arrayNext(hues, hue);
|
||||
let hueTweak = moreHue[relHue] ?? relHue + 'er';
|
||||
|
||||
ret[hue] = capitalize(hueTweak + ' ' + hue + 's');
|
||||
}
|
||||
|
||||
if (this.tweaked.grayChroma || this.tweaked.grayColor) {
|
||||
if (this.tweaked.grayChroma === 0) {
|
||||
ret.grayChroma = 'Achromatic grays';
|
||||
} else {
|
||||
if (this.tweaked.grayColor) {
|
||||
ret.grayColor = capitalize(this.grayColor) + ' gray undertone';
|
||||
}
|
||||
|
||||
if (this.tweaked.grayChroma) {
|
||||
let more = this.tweaked.grayChroma > this.originalGrayChroma;
|
||||
ret.grayChroma = `More ${more ? 'colorful' : 'neutral'} grays`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
},
|
||||
|
||||
originalContrasts() {
|
||||
return getContrasts(this.originalColors);
|
||||
},
|
||||
|
||||
contrasts() {
|
||||
return getContrasts(this.colors, this.originalContrasts);
|
||||
},
|
||||
|
||||
originalCoreColors() {
|
||||
let ret = {};
|
||||
for (let hue in this.originalColors) {
|
||||
let maxChromaTintRaw = this.originalColors[hue].maxChromaTintRaw;
|
||||
ret[hue] = this.originalColors[hue][maxChromaTintRaw];
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
|
||||
coreColors() {
|
||||
let ret = {};
|
||||
for (let hue in this.colors) {
|
||||
let maxChromaTintRaw = this.colors[hue].maxChromaTintRaw;
|
||||
ret[hue] = this.colors[hue][maxChromaTintRaw];
|
||||
}
|
||||
|
||||
return ret;
|
||||
},
|
||||
|
||||
originalGrayColor() {
|
||||
let grayHue = this.originalCoreColors.gray.get('h');
|
||||
let minDistance = Infinity;
|
||||
let closestHue = null;
|
||||
|
||||
for (let name in this.originalCoreColors) {
|
||||
if (name === 'gray') {
|
||||
continue;
|
||||
}
|
||||
|
||||
let hue = this.originalCoreColors[name].get('h');
|
||||
let distance = Math.abs(subtractAngles(hue, grayHue));
|
||||
if (distance < minDistance) {
|
||||
minDistance = distance;
|
||||
closestHue = name;
|
||||
}
|
||||
}
|
||||
|
||||
return closestHue ?? 'indigo';
|
||||
},
|
||||
|
||||
originalGrayChroma() {
|
||||
let coreTint = this.originalColors.gray.maxChromaTint;
|
||||
let grayChroma = this.originalColors.gray[coreTint].get('c');
|
||||
if (grayChroma === 0 || grayChroma === null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let grayColorChroma = this.originalColors[this.originalGrayColor][coreTint].get('c');
|
||||
return grayChroma / grayColorChroma;
|
||||
},
|
||||
|
||||
/**
|
||||
* We want to preserve the original grayChroma selection so that when the user switches to another undertone
|
||||
* that supports higher chromas, their selection will be there.
|
||||
* This property is the gray chroma % that is actually applied.
|
||||
*/
|
||||
computedGrayChroma() {
|
||||
return Math.min(this.grayChroma, this.maxGrayChroma);
|
||||
},
|
||||
|
||||
maxGrayChroma() {
|
||||
return maxGrayChroma[this.grayColor] ?? 0.3;
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
hueShifts: {
|
||||
deep: true,
|
||||
handler() {
|
||||
this.permalink.readFrom(this.hueShifts);
|
||||
},
|
||||
},
|
||||
|
||||
chromaScale() {
|
||||
this.permalink.set('chroma-scale', this.chromaScale, 1);
|
||||
},
|
||||
|
||||
grayColor() {
|
||||
this.permalink.set('gray-color', this.grayColor, this.originalGrayColor);
|
||||
},
|
||||
|
||||
grayChroma() {
|
||||
this.permalink.set('gray-chroma', this.grayChroma, this.originalGrayChroma);
|
||||
},
|
||||
|
||||
tweaks: {
|
||||
deep: true,
|
||||
async handler(value, oldValue) {
|
||||
await nextTick(); // must run after individual watchers
|
||||
|
||||
// Update page URL
|
||||
this.permalink.updateLocation();
|
||||
|
||||
if (this.saved) {
|
||||
this.save({ silent: true });
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
getPalette() {
|
||||
return { id: this.paletteId, uid: this.uid, search: location.search };
|
||||
},
|
||||
|
||||
save({ silent } = {}) {
|
||||
let title = silent
|
||||
? (this.saved?.title ?? this.paletteTitle)
|
||||
: prompt('Palette title:', `${this.paletteTitle} (tweaked)`);
|
||||
|
||||
if (!title) {
|
||||
return;
|
||||
}
|
||||
|
||||
let uid = this.uid;
|
||||
|
||||
if (!uid) {
|
||||
// First time saving
|
||||
this.uid = uid = sidebar.palette.getUid();
|
||||
|
||||
this.permalink.set('uid', uid);
|
||||
this.permalink.updateLocation();
|
||||
}
|
||||
|
||||
let palette = { ...this.getPalette(), uid, title };
|
||||
|
||||
sidebar.palette.save(palette, this.saved);
|
||||
this.saved = palette;
|
||||
},
|
||||
|
||||
rename() {
|
||||
if (!this.saved) {
|
||||
return;
|
||||
}
|
||||
|
||||
let newTitle = prompt('New title:', this.saved.title);
|
||||
|
||||
if (!newTitle) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.saved.title = newTitle;
|
||||
sidebar.palette.save(this.saved);
|
||||
},
|
||||
|
||||
deleteSaved() {
|
||||
sidebar.palette.delete(this.saved);
|
||||
},
|
||||
|
||||
postDelete() {
|
||||
this.saved = null;
|
||||
this.permalink.delete('uid');
|
||||
this.uid = undefined;
|
||||
this.permalink.updateLocation();
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove a specific tweak or all tweaks
|
||||
* @param {string} [param] - The tweak to remove. If not provided, all tweaks are removed.
|
||||
*/
|
||||
reset(param) {
|
||||
if (!param || param === 'chromaScale') {
|
||||
this.chromaScale = 1;
|
||||
}
|
||||
|
||||
if (param in this.hueShifts) {
|
||||
this.hueShifts[param] = 0;
|
||||
} else if (!param) {
|
||||
for (let hue in this.hueShifts) {
|
||||
this.hueShifts[hue] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!param || param === 'grayColor') {
|
||||
this.grayColor = this.originalGrayColor;
|
||||
}
|
||||
|
||||
if (!param || param === 'grayChroma') {
|
||||
this.grayChroma = this.originalGrayChroma;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
directives: {
|
||||
// Like v-text, but doesn't complain if the element has content,
|
||||
// making it possible to use in a PE fashion, with the contents being the fallback
|
||||
content(el, { value, arg }) {
|
||||
if (!el.dataset.fallback) {
|
||||
// Store the original content as a fallback the first time
|
||||
el.dataset.fallback = el.textContent;
|
||||
}
|
||||
|
||||
if (value === '') {
|
||||
value = el.dataset.fallback;
|
||||
} else {
|
||||
if (arg === 'number') {
|
||||
value = Number(value).toLocaleString(undefined, { maximumSignificantDigits: 2 });
|
||||
}
|
||||
}
|
||||
|
||||
if (arg === 'html') {
|
||||
el.innerHTML = value;
|
||||
} else {
|
||||
el.textContent = value;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
compilerOptions: {
|
||||
isCustomElement: tag => tag.startsWith('wa-'),
|
||||
},
|
||||
};
|
||||
|
||||
function init() {
|
||||
let paletteAppContainer = document.querySelector('#palette-app');
|
||||
globalThis.paletteApp?.unmount?.();
|
||||
|
||||
if (!paletteAppContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
globalThis.paletteApp = createApp(paletteAppSpec).mount(paletteAppContainer);
|
||||
}
|
||||
|
||||
init();
|
||||
addEventListener('turbo:render', init);
|
||||
|
||||
export function getPaletteCode(paletteId, colors, tweaked, options) {
|
||||
let imports = [];
|
||||
|
||||
if (paletteId) {
|
||||
imports.push(urls.palette(paletteId));
|
||||
}
|
||||
|
||||
let css = '';
|
||||
let declarations = [];
|
||||
|
||||
if (tweaked) {
|
||||
for (let hue in colors) {
|
||||
if (hue === 'orange') {
|
||||
continue;
|
||||
} else if (hue === 'gray') {
|
||||
if (!tweaked.grayChroma && !tweaked.grayColor) {
|
||||
continue;
|
||||
}
|
||||
} else if (!tweaked.chromaScale && !tweaked.hue?.[hue]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let tint of tints) {
|
||||
let color = colors[hue][tint];
|
||||
let stringified = color.toString({ format: color.inGamut('srgb') ? 'hex' : undefined });
|
||||
declarations.push(`--wa-color-${hue}-${tint}: ${stringified};`);
|
||||
}
|
||||
|
||||
declarations.push('');
|
||||
}
|
||||
|
||||
if (declarations.length > 0) {
|
||||
css += cssRule(selectors.palette(paletteId), declarations);
|
||||
}
|
||||
}
|
||||
|
||||
let ret = imports.map(url => cssImport(url, options)).join('\n');
|
||||
|
||||
if (css) {
|
||||
ret += `\n\n${cssLiteral(css, options)}`;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function arrayNext(array, element) {
|
||||
let index = array.indexOf(element);
|
||||
return array[(index + 1) % array.length];
|
||||
}
|
||||
|
||||
function arrayPrevious(array, element) {
|
||||
let index = array.indexOf(element);
|
||||
return array[(index - 1 + array.length) % array.length];
|
||||
}
|
||||
|
||||
function applyTweaks(originalColors, tweaks, tweaked) {
|
||||
let ret = {};
|
||||
let { hueShifts, chromaScale = 1, grayColor, grayChroma } = tweaks;
|
||||
|
||||
if (!tweaked) {
|
||||
return originalColors;
|
||||
}
|
||||
|
||||
if (tweaked.grayChroma) {
|
||||
grayChroma = this.computedGrayChroma;
|
||||
}
|
||||
|
||||
for (let hue in originalColors) {
|
||||
let originalScale = originalColors[hue];
|
||||
let scale = (ret[hue] = {});
|
||||
let descriptors = Object.getOwnPropertyDescriptors(originalScale);
|
||||
Object.defineProperties(scale, {
|
||||
maxChromaTint: { ...descriptors.maxChromaTint, enumerable: false },
|
||||
maxChromaTintRaw: { ...descriptors.maxChromaTintRaw, enumerable: false },
|
||||
});
|
||||
|
||||
for (let tint of tints) {
|
||||
let color = originalScale[tint].clone();
|
||||
|
||||
if (tweaked.hue && hueShifts[hue]) {
|
||||
color.set({ h: h => h + hueShifts[hue] });
|
||||
}
|
||||
|
||||
if (tweaked.chromaScale && chromaScale !== 1) {
|
||||
color.set({ c: c => c * chromaScale });
|
||||
}
|
||||
|
||||
if (hue === 'gray' && (tweaked.grayChroma || tweaked.grayColor)) {
|
||||
let colorUndertone = originalColors[grayColor][tint].clone();
|
||||
color = colorUndertone.set({ c: c => c * grayChroma });
|
||||
}
|
||||
|
||||
scale[tint] = color;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function camelCase(str) {
|
||||
return (str + '').replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
||||
}
|
||||
|
||||
function capitalize(str) {
|
||||
return str[0].toUpperCase() + str.slice(1);
|
||||
}
|
||||
|
||||
function getContrasts(colors, originalContrasts) {
|
||||
let ret = {};
|
||||
|
||||
for (let hue in colors) {
|
||||
ret[hue] = {};
|
||||
|
||||
for (let tintBg of tints) {
|
||||
ret[hue][tintBg] = {};
|
||||
let bgColor = colors[hue][tintBg];
|
||||
|
||||
if (!bgColor || !bgColor.contrast) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let tintFg of tints) {
|
||||
let fgColor = colors[hue][tintFg];
|
||||
let value = bgColor.contrast(fgColor, 'WCAG21');
|
||||
if (originalContrasts) {
|
||||
let original = originalContrasts[hue][tintBg][tintFg];
|
||||
ret[hue][tintBg][tintFg] = { value, original, bgColor, fgColor };
|
||||
} else {
|
||||
ret[hue][tintBg][tintFg] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -2,5 +2,7 @@
|
||||
title: Patterns
|
||||
description: Patterns are reusable solutions to common design problems.
|
||||
layout: overview
|
||||
categories: ["e-commerce"]
|
||||
listChildren: true
|
||||
override:tags: []
|
||||
---
|
||||
|
||||
@@ -16,8 +16,6 @@ During the alpha period, things might break! We take breaking changes very serio
|
||||
|
||||
- Fixed `wa-pill` class for text fields
|
||||
- Fixed `pill` style for `<wa-input>` elements
|
||||
- Fixed a bug in `<wa-color-picker>` that prevented light dismiss from working when clicking immediately above the color picker dropdown
|
||||
- Fixed a bug in `<wa-select multiple>` that sometimes resulted in empty `<div>` elements being output
|
||||
|
||||
## 3.0.0-alpha.11
|
||||
|
||||
@@ -27,7 +25,6 @@ During the alpha period, things might break! We take breaking changes very serio
|
||||
- Added a `pink` scale to all color palettes
|
||||
- Tweaked hues of all color palettes to make them more distinct and make their hues more intentional
|
||||
- Dropped `violet` and `teal`, instead using `purple` and `cyan` (this is not just a renaming, the colors have been adjusted too).
|
||||
- Fixed a bug in `<wa-switch>` that caused tooltips to work incorrectly when toggling the switch
|
||||
|
||||
### Design Tokens
|
||||
|
||||
|
||||
3
docs/docs/themes/creating.md
vendored
3
docs/docs/themes/creating.md
vendored
@@ -31,7 +31,8 @@ If you're customizing the default dark styles, scope your styles to the followin
|
||||
|
||||
```css
|
||||
.wa-dark,
|
||||
.wa-invert {
|
||||
.wa-invert,
|
||||
:is(:host-context(.wa-dark)) {
|
||||
/* your custom styles here */
|
||||
}
|
||||
```
|
||||
|
||||
9
docs/docs/themes/index.njk
vendored
9
docs/docs/themes/index.njk
vendored
@@ -1,13 +1,13 @@
|
||||
---
|
||||
title: Themes
|
||||
description: Themes are collections of design tokens that thread through every Web Awesome component and pattern.
|
||||
description: Themes are collections of design tokens that thread through every Web Awesome component and pattern.
|
||||
Themes play a crucial role in [customizing Web Awesome](/docs/customizing).
|
||||
layout: overview
|
||||
override:tags: []
|
||||
forTag: theme
|
||||
categories:
|
||||
tags: [other, pro]
|
||||
other: Free
|
||||
pro: Pro
|
||||
---
|
||||
|
||||
<div class="max-line-length">
|
||||
@@ -30,7 +30,7 @@ In pre-made themes, we use a light color scheme by default.
|
||||
Additionally, styles may be scoped to the `:root` selector to be activated automatically.
|
||||
For pre-made themes, *all* custom properties are scoped to `:root`, the theme class, and `wa-light`.
|
||||
|
||||
Finally, we scope themes to `:host` to ensure the styles are applied to the shadow roots of custom elements.
|
||||
Finally, we scope themes to `:host` and `:host-context()` to ensure the styles are applied to the shadow roots of custom elements.
|
||||
|
||||
For example, the default theme is set up like this:
|
||||
|
||||
@@ -44,7 +44,8 @@ For example, the default theme is set up like this:
|
||||
}
|
||||
|
||||
.wa-dark,
|
||||
.wa-invert {
|
||||
.wa-invert,
|
||||
:host-context(.wa-dark) {
|
||||
/* subset of CSS custom properties for a dark color scheme */
|
||||
}
|
||||
```
|
||||
|
||||
25
docs/docs/themes/remix.css
vendored
25
docs/docs/themes/remix.css
vendored
@@ -94,6 +94,31 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.selected-swatch,
|
||||
wa-select[name='brand'] wa-option::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 1.2em;
|
||||
aspect-ratio: 1;
|
||||
flex: none;
|
||||
border-radius: var(--wa-border-radius-m);
|
||||
background: var(--color);
|
||||
border: 1px solid var(--wa-color-surface-default);
|
||||
}
|
||||
|
||||
wa-select[name='brand'] wa-option {
|
||||
white-space: nowrap;
|
||||
|
||||
&::before {
|
||||
width: 1em;
|
||||
margin-inline: var(--wa-space-xs);
|
||||
}
|
||||
|
||||
&::part(checked-icon) {
|
||||
order: 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#test_select wa-option:state(selected) {
|
||||
|
||||
20
docs/docs/themes/remix.js
vendored
20
docs/docs/themes/remix.js
vendored
@@ -54,12 +54,8 @@ function init() {
|
||||
urlParams: new Permalink(),
|
||||
};
|
||||
|
||||
// Apply params from permalink
|
||||
for (let key in data.params) {
|
||||
if (data.urlParams.has(key)) {
|
||||
data.params[key] = data.urlParams.get(key);
|
||||
}
|
||||
}
|
||||
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
|
||||
@@ -125,22 +121,14 @@ function render(changedAspect) {
|
||||
|
||||
let brand = data.params.brand || data.defaultParams.brand;
|
||||
selects.brand.style.setProperty('--color', `var(--wa-color-${brand})`);
|
||||
|
||||
// Add current palette class and remove any other palette classes
|
||||
let paletteClass = `wa-palette-${computed.palette}`;
|
||||
selects.brand.className = selects.brand.className.replace(/\bwa-palette-[a-z]+\b/g, paletteClass);
|
||||
selects.brand.classList.add(paletteClass);
|
||||
selects.brand.className = `wa-palette-${computed.palette}`;
|
||||
|
||||
for (let aspect in data.params) {
|
||||
let value = data.params[aspect];
|
||||
selects[aspect].value = value;
|
||||
}
|
||||
|
||||
for (let key in data.params) {
|
||||
if (data.params[key]) {
|
||||
data.urlParams.set(key, data.params[key]);
|
||||
}
|
||||
}
|
||||
data.urlParams.readFrom(data.params);
|
||||
|
||||
// Update demo URL
|
||||
domChange(() => {
|
||||
|
||||
@@ -3,5 +3,4 @@ title: Design Tokens
|
||||
description: A theme is a collection of predefined CSS custom properties that control global styles from color to shadows. These custom properties thread through all Web Awesome components for a consistent look and feel.
|
||||
layout: overview
|
||||
override:tags: []
|
||||
categories: {tags: true}
|
||||
---
|
||||
|
||||
@@ -4,6 +4,8 @@ description: Build better with Web Awesome, the open source library of web compo
|
||||
layout: page
|
||||
---
|
||||
|
||||
|
||||
|
||||
<style>
|
||||
.title,
|
||||
.anchor-heading a,
|
||||
@@ -385,4 +387,4 @@ layout: page
|
||||
© Fonticons, Inc.
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,6 @@ import { execSync } from 'child_process';
|
||||
import { deleteAsync } from 'del';
|
||||
import esbuild from 'esbuild';
|
||||
import { replace } from 'esbuild-plugin-replace';
|
||||
|
||||
import { mkdir, readFile } from 'fs/promises';
|
||||
import getPort, { portNumbers } from 'get-port';
|
||||
import { globby } from 'globby';
|
||||
@@ -267,13 +266,6 @@ async function regenerateBundle() {
|
||||
* Generates the documentation site.
|
||||
*/
|
||||
async function generateDocs() {
|
||||
/**
|
||||
* Used by the webawesome-app to skip doc generation since it will do its own.
|
||||
*/
|
||||
if (process.env.SKIP_ELEVENTY === 'true') {
|
||||
return;
|
||||
}
|
||||
|
||||
spinner.start('Writing the docs');
|
||||
|
||||
const args = [];
|
||||
|
||||
@@ -109,7 +109,7 @@ export default class WaButton extends WebAwesomeFormAssociatedElement {
|
||||
@property({ reflect: true }) value: string | null = null;
|
||||
|
||||
/** When set, the underlying button will be rendered as an `<a>` with this `href` instead of a `<button>`. */
|
||||
@property({ reflect: true }) href = null;
|
||||
@property() href = '';
|
||||
|
||||
/** Tells the browser where to open the link. Only used when `href` is present. */
|
||||
@property() target: '_blank' | '_parent' | '_self' | '_top';
|
||||
|
||||
@@ -278,11 +278,11 @@
|
||||
}
|
||||
|
||||
/*
|
||||
* Color dropdown
|
||||
*/
|
||||
* Color dropdown
|
||||
*/
|
||||
|
||||
.color-dropdown {
|
||||
display: contents;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.color-dropdown::part(panel) {
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
:host {
|
||||
--background-color-hover: var(--wa-color-neutral-fill-quiet);
|
||||
--text-color-hover: color-mix(in oklab, currentColor, var(--wa-color-mix-hover));
|
||||
--background-color-active: transparent;
|
||||
--text-color-active: color-mix(in oklab, currentColor, var(--wa-color-mix-active));
|
||||
|
||||
display: inline-block;
|
||||
color: var(--wa-color-text-quiet);
|
||||
@@ -25,13 +22,12 @@
|
||||
|
||||
:host(:not([disabled])) .icon-button:hover,
|
||||
:host(:not([disabled])) .icon-button:focus-visible {
|
||||
background-color: var(--background-color-hover);
|
||||
color: var(--text-color-hover);
|
||||
background-color: var(--wa-color-neutral-fill-quiet);
|
||||
color: color-mix(in oklab, currentColor, var(--wa-color-mix-hover));
|
||||
}
|
||||
|
||||
:host(:not([disabled])) .icon-button:active {
|
||||
background-color: var(--background-color-active);
|
||||
color: var(--text-color-active);
|
||||
color: color-mix(in oklab, currentColor, var(--wa-color-mix-active));
|
||||
}
|
||||
|
||||
.icon-button:focus {
|
||||
|
||||
@@ -17,10 +17,7 @@ import styles from './icon-button.css';
|
||||
* @event blur - Emitted when the icon button loses focus.
|
||||
* @event focus - Emitted when the icon button gains focus.
|
||||
*
|
||||
* @cssproperty [--background-color-hover=var(--wa-color-neutral-fill-quiet)] - The color of the button's background on hover.
|
||||
* @cssproperty [--background-color-active=var(--wa-color-neutral-fill-quiet)] - The color of the button's background on `:active`.
|
||||
* @cssproperty --text-color-hover - The color of the button's background on hover.
|
||||
* @cssproperty --text-color-active - The color of the button's background on `:active`.
|
||||
* @cssproperty --background-color-hover - The color of the button's background on hover.
|
||||
*
|
||||
* @csspart base - The component's base wrapper.
|
||||
*/
|
||||
|
||||
@@ -647,7 +647,6 @@ describe('<wa-select>', () => {
|
||||
);
|
||||
const el = form.querySelector<WaSelect>('wa-select')!;
|
||||
|
||||
expect(el.defaultValue).to.equal('option-1');
|
||||
expect(el.value).to.equal('');
|
||||
expect(new FormData(form).get('select')).equal('');
|
||||
|
||||
@@ -658,7 +657,6 @@ describe('<wa-select>', () => {
|
||||
|
||||
await aTimeout(10);
|
||||
await el.updateComplete;
|
||||
expect(el.optionValues ? [...el.optionValues] : []).to.have.members(['option-1']);
|
||||
expect(el.value).to.equal('option-1');
|
||||
expect(new FormData(form).get('select')).equal('option-1');
|
||||
});
|
||||
@@ -747,8 +745,6 @@ describe('<wa-select>', () => {
|
||||
);
|
||||
|
||||
const el = form.querySelector<WaSelect>('wa-select')!;
|
||||
expect(el.optionValues ? [...el.optionValues] : []).to.have.members(['bar', 'baz']);
|
||||
expect(el.optionValues?.size).to.equal(2);
|
||||
expect(el.value).to.have.members(['bar', 'baz']);
|
||||
expect(el.value!.length).to.equal(2);
|
||||
expect(new FormData(form).getAll('select')).to.have.members(['bar', 'baz']);
|
||||
@@ -764,36 +760,6 @@ describe('<wa-select>', () => {
|
||||
expect(new FormData(form).getAll('select')).to.have.members(['foo', 'bar', 'baz']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('With setting the value via JS', () => {
|
||||
it('Should preserve value even if not returned', async () => {
|
||||
const form = await fixture<HTMLFormElement>(
|
||||
html` <form>
|
||||
<wa-select name="select">
|
||||
<wa-option value="bar">Bar</wa-option>
|
||||
<wa-option value="baz">Baz</wa-option>
|
||||
</wa-select>
|
||||
</form>`,
|
||||
);
|
||||
|
||||
const el = form.querySelector<WaSelect>('wa-select')!;
|
||||
expect(el.value).to.equal('');
|
||||
|
||||
el.value = 'foo';
|
||||
await aTimeout(10);
|
||||
await el.updateComplete;
|
||||
expect(el.value).to.equal('');
|
||||
|
||||
const option = document.createElement('wa-option');
|
||||
option.value = 'foo';
|
||||
option.innerText = 'Foo';
|
||||
el.append(option);
|
||||
|
||||
await aTimeout(10);
|
||||
await el.updateComplete;
|
||||
expect(el.value).to.equal('foo');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -118,7 +118,6 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
||||
@state() displayLabel = '';
|
||||
@state() currentOption: WaOption;
|
||||
@state() selectedOptions: WaOption[] = [];
|
||||
@state() optionValues: Set<string> | undefined;
|
||||
|
||||
/** The name of the select, submitted as a name/value pair with form data. */
|
||||
@property() name = '';
|
||||
@@ -159,47 +158,7 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
||||
return val;
|
||||
}
|
||||
|
||||
private _value: string[] | undefined;
|
||||
@property({ attribute: false })
|
||||
set value(val: string | string[]) {
|
||||
let oldValue = this.value;
|
||||
|
||||
if (!Array.isArray(val)) {
|
||||
val = val.split(' ');
|
||||
}
|
||||
|
||||
if (!this._value || this._value.join(' ') !== val.join(' ')) {
|
||||
this._value = val;
|
||||
let newValue = this.value;
|
||||
|
||||
if (newValue != oldValue) {
|
||||
this.requestUpdate('value', oldValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
get value() {
|
||||
let value = this._value ?? this.defaultValue;
|
||||
value = Array.isArray(value) ? value : [value];
|
||||
let optionsChanged = !this.optionValues;
|
||||
|
||||
if (optionsChanged) {
|
||||
this.optionValues = new Set(
|
||||
this.getAllOptions()
|
||||
.filter(option => !option.disabled)
|
||||
.map(option => option.value),
|
||||
);
|
||||
}
|
||||
|
||||
// Drop values not in the DOM
|
||||
let ret: string | string[] = value.filter(v => this.optionValues!.has(v));
|
||||
ret = this.multiple ? ret : (ret[0] ?? '');
|
||||
|
||||
if (optionsChanged) {
|
||||
this.requestUpdate('value');
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@property({ attribute: false }) value: string | string[] | null = null;
|
||||
|
||||
/** The select's size. */
|
||||
@property({ reflect: true, initial: 'medium' }) size: 'small' | 'medium' | 'large' | 'inherit' = 'inherit';
|
||||
@@ -291,6 +250,7 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
||||
?pill=${this.pill}
|
||||
size=${this.size}
|
||||
removable
|
||||
@wa-remove=${(event: WaRemoveEvent) => this.handleTagRemove(event, option)}
|
||||
>
|
||||
${option.label}
|
||||
</wa-tag>
|
||||
@@ -578,41 +538,21 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
||||
}
|
||||
|
||||
const allOptions = this.getAllOptions();
|
||||
this.optionValues = undefined; // dirty the value so it gets recalculated
|
||||
const val = this.valueHasChanged ? this.value : this.defaultValue;
|
||||
const value = Array.isArray(val) ? val : [val];
|
||||
const values: string[] = [];
|
||||
|
||||
const value = this.value;
|
||||
// Check for duplicate values in menu items
|
||||
allOptions.forEach(option => values.push(option.value));
|
||||
|
||||
// Select only the options that match the new value
|
||||
this.setSelectedOptions(allOptions.filter(el => value.includes(el.value)));
|
||||
}
|
||||
|
||||
private handleTagRemove(event: WaRemoveEvent, directOption?: WaOption) {
|
||||
private handleTagRemove(event: WaRemoveEvent, option: WaOption) {
|
||||
event.stopPropagation();
|
||||
|
||||
if (this.disabled) return;
|
||||
|
||||
// Use the directly provided option if available (from getTag method)
|
||||
let option = directOption;
|
||||
|
||||
// If no direct option was provided, find the option from the event path
|
||||
if (!option) {
|
||||
const tagElement = (event.target as Element).closest('wa-tag[part~=tag]');
|
||||
|
||||
if (tagElement) {
|
||||
// Find the index of this tag among all tags
|
||||
const tagsContainer = this.shadowRoot?.querySelector('[part="tags"]');
|
||||
if (tagsContainer) {
|
||||
const allTags = Array.from(tagsContainer.children);
|
||||
const index = allTags.indexOf(tagElement as HTMLElement);
|
||||
|
||||
if (index >= 0 && index < this.selectedOptions.length) {
|
||||
option = this.selectedOptions[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (option) {
|
||||
if (!this.disabled) {
|
||||
this.toggleOptionSelection(option, false);
|
||||
|
||||
// Emit after updating
|
||||
@@ -625,9 +565,6 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
||||
|
||||
// Gets an array of all `<wa-option>` elements
|
||||
private getAllOptions() {
|
||||
if (!this?.querySelectorAll) {
|
||||
return [];
|
||||
}
|
||||
return [...this.querySelectorAll<WaOption>('wa-option')];
|
||||
}
|
||||
|
||||
@@ -691,24 +628,11 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
||||
|
||||
// Update selected options cache
|
||||
this.selectedOptions = options.filter(el => el.selected);
|
||||
let selectedValues = new Set(this.selectedOptions.map(el => el.value));
|
||||
|
||||
// Toggle values present in the DOM from this.value, while preserving options NOT present in the DOM (for lazy loading)
|
||||
// Note that options NOT present in the DOM will be moved to the end after this
|
||||
if (selectedValues.size > 0 || this._value) {
|
||||
if (!this._value) {
|
||||
// First time it's set
|
||||
let value = this.defaultValue ?? [];
|
||||
this._value = Array.isArray(value) ? value : [value];
|
||||
}
|
||||
|
||||
// Filter out values that are in the DOM
|
||||
this._value = this._value.filter(value => !this.optionValues?.has(value));
|
||||
this._value.unshift(...selectedValues);
|
||||
}
|
||||
|
||||
// Update the value and display label
|
||||
if (this.multiple) {
|
||||
this.value = this.selectedOptions.map(el => el.value);
|
||||
|
||||
if (this.placeholder && this.value.length === 0) {
|
||||
// When no items are selected, keep the value empty so the placeholder shows
|
||||
this.displayLabel = '';
|
||||
@@ -717,6 +641,7 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
||||
}
|
||||
} else {
|
||||
const selectedOption = this.selectedOptions[0];
|
||||
this.value = selectedOption?.value ?? '';
|
||||
this.displayLabel = selectedOption?.label ?? '';
|
||||
}
|
||||
|
||||
@@ -729,8 +654,10 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
||||
return this.selectedOptions.map((option, index) => {
|
||||
if (index < this.maxOptionsVisible || this.maxOptionsVisible <= 0) {
|
||||
const tag = this.getTag(option, index);
|
||||
if (!tag) return null;
|
||||
return typeof tag === 'string' ? unsafeHTML(tag) : tag;
|
||||
// Wrap so we can handle the remove
|
||||
return html`<div @wa-remove=${(e: WaRemoveEvent) => this.handleTagRemove(e, option)}>
|
||||
${typeof tag === 'string' ? unsafeHTML(tag) : tag}
|
||||
</div>`;
|
||||
} else if (index === this.maxOptionsVisible) {
|
||||
// Hit tag limit
|
||||
return html`
|
||||
@@ -746,7 +673,7 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
||||
>
|
||||
`;
|
||||
}
|
||||
return null;
|
||||
return html``;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -946,9 +873,7 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
||||
/>
|
||||
|
||||
<!-- Tags need to wait for first hydration before populating otherwise it will create a hydration mismatch. -->
|
||||
${this.multiple && this.hasUpdated
|
||||
? html`<div part="tags" class="tags" @wa-remove=${this.handleTagRemove}>${this.tags}</div>`
|
||||
: ''}
|
||||
${this.multiple && this.hasUpdated ? html`<div part="tags" class="tags">${this.tags}</div>` : ''}
|
||||
|
||||
<input
|
||||
class="value-input"
|
||||
|
||||
@@ -49,7 +49,6 @@ import styles from './switch.css';
|
||||
*/
|
||||
@customElement('wa-switch')
|
||||
export default class WaSwitch extends WebAwesomeFormAssociatedElement {
|
||||
static shadowRootOptions = { ...WebAwesomeFormAssociatedElement.shadowRootOptions, delegatesFocus: true };
|
||||
static shadowStyle = [formControlStyles, sizeStyles, styles];
|
||||
|
||||
static get validators() {
|
||||
|
||||
74
src/styles/brand/base.css
Normal file
74
src/styles/brand/base.css
Normal file
@@ -0,0 +1,74 @@
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
:where([class^='wa-palette-'], [class*=' wa-palette-']),
|
||||
:where([class^='wa-brand-'], [class*=' wa-brand-']) {
|
||||
/**
|
||||
* Conditional tokens for use in color-mix()
|
||||
* --wa-color-brand-if-lt-N ➡️ 100% if key < N, 0% otherwise
|
||||
* --wa-color-brand-if-gte-N ➡️ 100% if key >= N, 0% otherwise
|
||||
*/
|
||||
--wa-color-brand-if-lt-40: calc(clamp(0, 40 - var(--wa-color-brand-key), 1) * 100%);
|
||||
--wa-color-brand-if-lt-50: calc(clamp(0, 50 - var(--wa-color-brand-key), 1) * 100%);
|
||||
--wa-color-brand-if-lt-60: calc(clamp(0, 60 - var(--wa-color-brand-key), 1) * 100%);
|
||||
--wa-color-brand-if-lt-70: calc(clamp(0, 70 - var(--wa-color-brand-key), 1) * 100%);
|
||||
--wa-color-brand-if-lt-80: calc(clamp(0, 80 - var(--wa-color-brand-key), 1) * 100%);
|
||||
|
||||
--wa-color-brand-if-gte-40: calc(100% - var(--wa-color-brand-if-lt-40));
|
||||
--wa-color-brand-if-gte-50: calc(100% - var(--wa-color-brand-if-lt-50));
|
||||
--wa-color-brand-if-gte-60: calc(100% - var(--wa-color-brand-if-lt-60));
|
||||
--wa-color-brand-if-gte-70: calc(100% - var(--wa-color-brand-if-lt-70));
|
||||
--wa-color-brand-if-gte-80: calc(100% - var(--wa-color-brand-if-lt-80));
|
||||
|
||||
/*
|
||||
* Convenience tokens for common tint cutoffs
|
||||
* --wa-color-brand-N-max ➡️ var(--color-brand) if key <= N, var(--color-brand-N) otherwise
|
||||
* --wa-color-brand-N-min ➡️ var(--color-brand) if key >= N, var(--color-brand-N) otherwise
|
||||
*/
|
||||
--wa-color-brand-40-max: color-mix(
|
||||
in oklab,
|
||||
var(--wa-color-brand) var(--wa-color-brand-if-lt-40),
|
||||
var(--wa-color-brand-40)
|
||||
);
|
||||
--wa-color-brand-40-min: color-mix(
|
||||
in oklab,
|
||||
var(--wa-color-brand) var(--wa-color-brand-if-gte-40),
|
||||
var(--wa-color-brand-40)
|
||||
);
|
||||
|
||||
--wa-color-brand-50-max: color-mix(
|
||||
in oklab,
|
||||
var(--wa-color-brand) var(--wa-color-brand-if-lt-50),
|
||||
var(--wa-color-brand-50)
|
||||
);
|
||||
--wa-color-brand-50-min: color-mix(
|
||||
in oklab,
|
||||
var(--wa-color-brand) var(--wa-color-brand-if-gte-50),
|
||||
var(--wa-color-brand-50)
|
||||
);
|
||||
|
||||
--wa-color-brand-60-max: color-mix(
|
||||
in oklab,
|
||||
var(--wa-color-brand) var(--wa-color-brand-if-lt-60),
|
||||
var(--wa-color-brand-60)
|
||||
);
|
||||
--wa-color-brand-60-min: color-mix(
|
||||
in oklab,
|
||||
var(--wa-color-brand) var(--wa-color-brand-if-gte-60),
|
||||
var(--wa-color-brand-60)
|
||||
);
|
||||
|
||||
--wa-color-brand-70-max: color-mix(
|
||||
in oklab,
|
||||
var(--wa-color-brand) var(--wa-color-brand-if-lt-70),
|
||||
var(--wa-color-brand-70)
|
||||
);
|
||||
--wa-color-brand-70-min: color-mix(
|
||||
in oklab,
|
||||
var(--wa-color-brand) var(--wa-color-brand-if-gte-70),
|
||||
var(--wa-color-brand-70)
|
||||
);
|
||||
|
||||
/* Text color: white if key < 60, brand-10 otherwise */
|
||||
--wa-color-brand-on: color-mix(in oklab, var(--wa-color-brand-10) var(--wa-color-brand-if-gte-60), white);
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
@import url('base.css');
|
||||
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
@@ -15,22 +17,5 @@
|
||||
--wa-color-brand-10: var(--wa-color-blue-10);
|
||||
--wa-color-brand-05: var(--wa-color-blue-05);
|
||||
--wa-color-brand: var(--wa-color-blue);
|
||||
|
||||
--wa-color-brand-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-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);
|
||||
--wa-color-brand-key: var(--wa-color-blue-key);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
@import url('base.css');
|
||||
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
@@ -15,22 +17,5 @@
|
||||
--wa-color-brand-10: var(--wa-color-cyan-10);
|
||||
--wa-color-brand-05: var(--wa-color-cyan-05);
|
||||
--wa-color-brand: var(--wa-color-cyan);
|
||||
|
||||
--wa-color-brand-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-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);
|
||||
--wa-color-brand-key: var(--wa-color-cyan-key);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
@import url('base.css');
|
||||
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
@@ -15,22 +17,5 @@
|
||||
--wa-color-brand-10: var(--wa-color-gray-10);
|
||||
--wa-color-brand-05: var(--wa-color-gray-05);
|
||||
--wa-color-brand: var(--wa-color-gray);
|
||||
|
||||
--wa-color-brand-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-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);
|
||||
--wa-color-brand-key: var(--wa-color-gray-key);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
@import url('base.css');
|
||||
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
@@ -15,22 +17,5 @@
|
||||
--wa-color-brand-10: var(--wa-color-green-10);
|
||||
--wa-color-brand-05: var(--wa-color-green-05);
|
||||
--wa-color-brand: var(--wa-color-green);
|
||||
|
||||
--wa-color-brand-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-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);
|
||||
--wa-color-brand-key: var(--wa-color-green-key);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
@import url('base.css');
|
||||
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
@@ -15,22 +17,5 @@
|
||||
--wa-color-brand-10: var(--wa-color-indigo-10);
|
||||
--wa-color-brand-05: var(--wa-color-indigo-05);
|
||||
--wa-color-brand: var(--wa-color-indigo);
|
||||
|
||||
--wa-color-brand-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-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);
|
||||
--wa-color-brand-key: var(--wa-color-indigo-key);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
@import url('base.css');
|
||||
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
@@ -15,22 +17,5 @@
|
||||
--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-lt-50: var(--wa-color-orange-lt-50);
|
||||
--wa-color-brand-gte-50: var(--wa-color-orange-gte-50);
|
||||
|
||||
--wa-color-brand-lt-60: var(--wa-color-orange-lt-60);
|
||||
--wa-color-brand-gte-60: var(--wa-color-orange-gte-60);
|
||||
|
||||
--wa-color-brand-lt-70: var(--wa-color-orange-lt-70);
|
||||
--wa-color-brand-gte-70: var(--wa-color-orange-gte-70);
|
||||
|
||||
--wa-color-brand-max-50: var(--wa-color-orange-max-50);
|
||||
--wa-color-brand-min-50: var(--wa-color-orange-min-50);
|
||||
--wa-color-brand-max-60: var(--wa-color-orange-max-60);
|
||||
--wa-color-brand-min-60: var(--wa-color-orange-min-60);
|
||||
--wa-color-brand-max-70: var(--wa-color-orange-max-70);
|
||||
--wa-color-brand-min-70: var(--wa-color-orange-min-70);
|
||||
|
||||
--wa-color-brand-on: var(--wa-color-orange-on);
|
||||
--wa-color-brand-key: var(--wa-color-orange-key);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
@import url('base.css');
|
||||
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
@@ -15,22 +17,5 @@
|
||||
--wa-color-brand-10: var(--wa-color-pink-10);
|
||||
--wa-color-brand-05: var(--wa-color-pink-05);
|
||||
--wa-color-brand: var(--wa-color-pink);
|
||||
|
||||
--wa-color-brand-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-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);
|
||||
--wa-color-brand-key: var(--wa-color-pink-key);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
@import url('base.css');
|
||||
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
@@ -15,22 +17,5 @@
|
||||
--wa-color-brand-10: var(--wa-color-purple-10);
|
||||
--wa-color-brand-05: var(--wa-color-purple-05);
|
||||
--wa-color-brand: var(--wa-color-purple);
|
||||
|
||||
--wa-color-brand-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-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);
|
||||
--wa-color-brand-key: var(--wa-color-purple-key);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
@import url('base.css');
|
||||
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
@@ -15,22 +17,5 @@
|
||||
--wa-color-brand-10: var(--wa-color-red-10);
|
||||
--wa-color-brand-05: var(--wa-color-red-05);
|
||||
--wa-color-brand: var(--wa-color-red);
|
||||
|
||||
--wa-color-brand-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-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);
|
||||
--wa-color-brand-key: var(--wa-color-red-key);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
@import url('base.css');
|
||||
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
@@ -15,22 +17,5 @@
|
||||
--wa-color-brand-10: var(--wa-color-yellow-10);
|
||||
--wa-color-brand-05: var(--wa-color-yellow-05);
|
||||
--wa-color-brand: var(--wa-color-yellow);
|
||||
|
||||
--wa-color-brand-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-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);
|
||||
--wa-color-brand-key: var(--wa-color-yellow-key);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
@import url('base.css');
|
||||
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
@@ -18,17 +16,17 @@
|
||||
--wa-color-red: var(--wa-color-red-70);
|
||||
--wa-color-red-key: 70;
|
||||
|
||||
--wa-color-orange-95: #ffefe8 /* oklch(96.287% 0.01978 45.262) */;
|
||||
--wa-color-orange-90: #ffe0d2 /* oklch(92.854% 0.03927 45.816) */;
|
||||
--wa-color-orange-80: #fbbc9e /* oklch(84.557% 0.08373 46.569) */;
|
||||
--wa-color-orange-70: #f3976f /* oklch(76.236% 0.12363 43.995) */;
|
||||
--wa-color-orange-60: #d67e59 /* oklch(68.046% 0.12088 43.174) */;
|
||||
--wa-color-orange-50: #ae6140 /* oklch(57.433% 0.11041 43.333) */;
|
||||
--wa-color-orange-40: #89472c /* oklch(47.39% 0.09837 42.278) */;
|
||||
--wa-color-orange-30: #6f351e /* oklch(40.084% 0.08944 41.51) */;
|
||||
--wa-color-orange-20: #542513 /* oklch(32.696% 0.07578 40.822) */;
|
||||
--wa-color-orange-10: #341408 /* oklch(23.762% 0.05577 40.619) */;
|
||||
--wa-color-orange-05: #230b04 /* oklch(18.679% 0.04417 39.815) */;
|
||||
--wa-color-orange-95: oklch(96.462% 0.02077 52.138);
|
||||
--wa-color-orange-90: oklch(92.556% 0.04363 51.242);
|
||||
--wa-color-orange-80: #fdbb96 /* oklch(84.396% 0.09052 50.397) */;
|
||||
--wa-color-orange-70: #eb9c74 /* oklch(76.151% 0.10953 47.299) */;
|
||||
--wa-color-orange-60: #cf8162 /* oklch(67.86% 0.10698 42.148) */;
|
||||
--wa-color-orange-50: #aa6248 /* oklch(57.281% 0.1014 40.415) */;
|
||||
--wa-color-orange-40: #864834 /* oklch(47.233% 0.08958 39.166) */;
|
||||
--wa-color-orange-30: #6b3727 /* oklch(39.974% 0.07776 38.975) */;
|
||||
--wa-color-orange-20: #50271a /* oklch(32.519% 0.0649 38.022) */;
|
||||
--wa-color-orange-10: #32160e /* oklch(23.873% 0.04713 38.423) */;
|
||||
--wa-color-orange-05: #210c06 /* oklch(18.614% 0.03797 38.589) */;
|
||||
--wa-color-orange: var(--wa-color-orange-70);
|
||||
--wa-color-orange-key: 70;
|
||||
|
||||
|
||||
@@ -1,338 +0,0 @@
|
||||
: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-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-orange-lt-50: calc(clamp(0, 50 - var(--wa-color-orange-key), 1) * 100%);
|
||||
--wa-color-orange-gte-50: calc(100% - var(--wa-color-orange-lt-50));
|
||||
--wa-color-orange-lt-60: calc(clamp(0, 60 - var(--wa-color-orange-key), 1) * 100%);
|
||||
--wa-color-orange-gte-60: calc(100% - var(--wa-color-orange-lt-60));
|
||||
--wa-color-orange-lt-70: calc(clamp(0, 70 - var(--wa-color-orange-key), 1) * 100%);
|
||||
--wa-color-orange-gte-70: calc(100% - var(--wa-color-orange-lt-70));
|
||||
|
||||
--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-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-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-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-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-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-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-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));
|
||||
|
||||
/*
|
||||
* 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-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-orange-max-50: color-mix(
|
||||
in oklab,
|
||||
var(--wa-color-orange) var(--wa-color-orange-lt-50),
|
||||
var(--wa-color-orange-50)
|
||||
);
|
||||
--wa-color-orange-min-50: color-mix(
|
||||
in oklab,
|
||||
var(--wa-color-orange) var(--wa-color-orange-gte-50),
|
||||
var(--wa-color-orange-50)
|
||||
);
|
||||
--wa-color-orange-max-60: color-mix(
|
||||
in oklab,
|
||||
var(--wa-color-orange) var(--wa-color-orange-lt-60),
|
||||
var(--wa-color-orange-60)
|
||||
);
|
||||
--wa-color-orange-min-60: color-mix(
|
||||
in oklab,
|
||||
var(--wa-color-orange) var(--wa-color-orange-gte-60),
|
||||
var(--wa-color-orange-60)
|
||||
);
|
||||
--wa-color-orange-max-70: color-mix(
|
||||
in oklab,
|
||||
var(--wa-color-orange) var(--wa-color-orange-lt-70),
|
||||
var(--wa-color-orange-70)
|
||||
);
|
||||
--wa-color-orange-min-70: color-mix(
|
||||
in oklab,
|
||||
var(--wa-color-orange) var(--wa-color-orange-gte-70),
|
||||
var(--wa-color-orange-70)
|
||||
);
|
||||
|
||||
--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-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-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-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-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-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-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-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-orange-on: color-mix(in oklab, var(--wa-color-orange-10) var(--wa-color-orange-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);
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
@import url('base.css');
|
||||
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
@@ -22,13 +20,13 @@
|
||||
--wa-color-orange-90: oklch(92.395% 0.07984 53.06);
|
||||
--wa-color-orange-80: oklch(84.389% 0.12224 47.981);
|
||||
--wa-color-orange-70: oklch(76.55% 0.16521 42.512);
|
||||
--wa-color-orange-60: #e97331 /* oklch(68.357% 0.16507 46.504) */;
|
||||
--wa-color-orange-50: #bf5712 /* oklch(57.834% 0.15197 47.326) */;
|
||||
--wa-color-orange-40: oklch(47.62% 0.132 48.51);
|
||||
--wa-color-orange-30: oklch(40.38% 0.11554 50);
|
||||
--wa-color-orange-20: oklch(32.94% 0.09927 52);
|
||||
--wa-color-orange-10: oklch(24.083% 0.07743 54);
|
||||
--wa-color-orange-05: oklch(18.817% 0.06098 55);
|
||||
--wa-color-orange-60: #ea7237 /* oklch(68.444% 0.16501 44.349) */;
|
||||
--wa-color-orange-50: #c0561a /* oklch(57.844% 0.15254 45.085) */;
|
||||
--wa-color-orange-40: #963e05 /* oklch(47.639% 0.13153 45.898) */;
|
||||
--wa-color-orange-30: oklch(40.376% 0.11554 45.517);
|
||||
--wa-color-orange-20: oklch(32.94% 0.09927 45.913);
|
||||
--wa-color-orange-10: oklch(24.083% 0.07743 46.027);
|
||||
--wa-color-orange-05: oklch(18.817% 0.06098 48.455);
|
||||
--wa-color-orange: var(--wa-color-orange-70);
|
||||
--wa-color-orange-key: 70;
|
||||
|
||||
|
||||
@@ -1,50 +1,48 @@
|
||||
@import url('base.css');
|
||||
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
.wa-palette-classic {
|
||||
--wa-color-red-95: #fff0ef /* oklch(96.667% 0.01632 22.08) */;
|
||||
--wa-color-red-90: #ffdedc /* oklch(92.735% 0.03679 21.966) */;
|
||||
--wa-color-red-80: #ffb8b5 /* oklch(84.778% 0.083 21.686) */;
|
||||
--wa-color-red-70: #fe8f8d /* oklch(76.859% 0.13466 21.762) */;
|
||||
--wa-color-red-60: #f56667 /* oklch(68.982% 0.17631 22.472) */;
|
||||
--wa-color-red-50: #e02c2b /* oklch(58.861% 0.21461 27.156) */;
|
||||
--wa-color-red-40: #b5051a /* oklch(48.833% 0.19611 25.68) */;
|
||||
--wa-color-red-30: #900015 /* oklch(41.172% 0.16676 24.609) */;
|
||||
--wa-color-red-20: #6c000d /* oklch(33.479% 0.1356 24.617) */;
|
||||
--wa-color-red-10: #450005 /* oklch(24.598% 0.09968 24.835) */;
|
||||
--wa-color-red-05: #2f0002 /* oklch(19.218% 0.07801 25.517) */;
|
||||
--wa-color-red-80: #ffb8b4 /* oklch(84.753% 0.08313 22.598) */;
|
||||
--wa-color-red-70: #fd908e /* oklch(76.913% 0.13219 21.705) */;
|
||||
--wa-color-red-60: #f46766 /* oklch(68.945% 0.17423 23.179) */;
|
||||
--wa-color-red-50: #df2f2e /* oklch(58.922% 0.21141 26.911) */;
|
||||
--wa-color-red-40: #b60000 /* oklch(48.747% 0.20003 29.234) */;
|
||||
--wa-color-red-30: #910000 /* oklch(41.235% 0.16921 29.234) */;
|
||||
--wa-color-red-20: #6d0000 /* oklch(33.581% 0.1378 29.234) */;
|
||||
--wa-color-red-10: #450000 /* oklch(24.517% 0.1006 29.234) */;
|
||||
--wa-color-red-05: #2f0000 /* oklch(19.165% 0.07864 29.234) */;
|
||||
--wa-color-red: var(--wa-color-red-50);
|
||||
--wa-color-red-key: 50;
|
||||
|
||||
--wa-color-orange-95: #fff0e4 /* oklch(96.374% 0.0228 61.238) */;
|
||||
--wa-color-orange-90: #ffe0c8 /* oklch(92.611% 0.04689 59.917) */;
|
||||
--wa-color-orange-80: #ffbb89 /* oklch(84.386% 0.10217 57.161) */;
|
||||
--wa-color-orange-70: #ff9342 /* oklch(76.486% 0.15964 54.102) */;
|
||||
--wa-color-orange-60: #f36d00 /* oklch(68.715% 0.18774 47.79) */;
|
||||
--wa-color-orange-50: #c94e00 /* oklch(58.068% 0.17131 43.217) */;
|
||||
--wa-color-orange-40: #9d3800 /* oklch(47.924% 0.14534 41.739) */;
|
||||
--wa-color-orange-30: #7e2900 /* oklch(40.53% 0.1259 40.51) */;
|
||||
--wa-color-orange-20: #5e1c00 /* oklch(32.874% 0.1027 40.228) */;
|
||||
--wa-color-orange-10: #3b0f00 /* oklch(24.125% 0.07446 40.837) */;
|
||||
--wa-color-orange-05: #280700 /* oklch(18.837% 0.05933 39.827) */;
|
||||
--wa-color-orange-95: oklch(96.245% 0.04153 59.224);
|
||||
--wa-color-orange-90: oklch(92.468% 0.07656 58.647);
|
||||
--wa-color-orange-80: oklch(84.375% 0.11283 54.179);
|
||||
--wa-color-orange-70: #fb945a /* oklch(76.369% 0.14454 48.621) */;
|
||||
--wa-color-orange-60: #f26b31 /* oklch(68.509% 0.18046 41.503) */;
|
||||
--wa-color-orange-50: #cf4812 /* oklch(58.288% 0.18026 38.576) */;
|
||||
--wa-color-orange-40: oklch(48.175% 0.16316 38.526);
|
||||
--wa-color-orange-30: oklch(40.779% 0.13925 37.899);
|
||||
--wa-color-orange-20: oklch(33.129% 0.11288 38.58);
|
||||
--wa-color-orange-10: oklch(24.259% 0.0831 38.502);
|
||||
--wa-color-orange-05: oklch(18.969% 0.06527 38.137);
|
||||
--wa-color-orange: var(--wa-color-orange-60);
|
||||
--wa-color-orange-key: 60;
|
||||
|
||||
--wa-color-yellow-95: #fef2bf /* oklch(95.823% 0.06674 96.369) */;
|
||||
--wa-color-yellow-90: #fde588 /* oklch(92.2% 0.11633 95.327) */;
|
||||
--wa-color-yellow-80: #f5c24b /* oklch(83.879% 0.14445 85.083) */;
|
||||
--wa-color-yellow-80: #f4c34e /* oklch(83.998% 0.14252 85.76) */;
|
||||
--wa-color-yellow-70: #e9a010 /* oklch(75.825% 0.15689 75.537) */;
|
||||
--wa-color-yellow-60: #d78000 /* oklch(67.839% 0.15287 64.455) */;
|
||||
--wa-color-yellow-50: #b26000 /* oklch(57.324% 0.13672 58.338) */;
|
||||
--wa-color-yellow-40: #8a4700 /* oklch(47.183% 0.11461 56.655) */;
|
||||
--wa-color-yellow-30: #6e3700 /* oklch(40.03% 0.0976 56.323) */;
|
||||
--wa-color-yellow-20: #522700 /* oklch(32.54% 0.07981 55.802) */;
|
||||
--wa-color-yellow-10: #331600 /* oklch(23.846% 0.05834 56.02) */;
|
||||
--wa-color-yellow-05: #210d00 /* oklch(18.619% 0.04431 58.553) */;
|
||||
--wa-color-yellow: var(--wa-color-yellow-70);
|
||||
--wa-color-yellow-key: 70;
|
||||
--wa-color-yellow-60: #de7c00 /* oklch(68.073% 0.15991 59.827) */;
|
||||
--wa-color-yellow-50: #bc5908 /* oklch(57.654% 0.1491 50.241) */;
|
||||
--wa-color-yellow-40: #934107 /* oklch(47.603% 0.12628 47.819) */;
|
||||
--wa-color-yellow-30: #763104 /* oklch(40.324% 0.1093 46.564) */;
|
||||
--wa-color-yellow-20: #572301 /* oklch(32.677% 0.08796 47.926) */;
|
||||
--wa-color-yellow-10: #371300 /* oklch(24.001% 0.06559 47.77) */;
|
||||
--wa-color-yellow-05: #250a00 /* oklch(18.773% 0.0519 47.039) */;
|
||||
--wa-color-yellow: var(--wa-color-yellow-60);
|
||||
--wa-color-yellow-key: 60;
|
||||
|
||||
--wa-color-green-95: #d4fce1 /* oklch(95.554% 0.05477 155.71) */;
|
||||
--wa-color-green-90: #a4f8c2 /* oklch(91.11% 0.1107 155.35) */;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
@import url('base.css');
|
||||
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
@@ -18,19 +16,19 @@
|
||||
--wa-color-red: var(--wa-color-red-50);
|
||||
--wa-color-red-key: 50;
|
||||
|
||||
--wa-color-orange-95: #fff0e6 /* oklch(96.426% 0.02105 56.133) */;
|
||||
--wa-color-orange-90: #ffdfca /* oklch(92.468% 0.04529 55.325) */;
|
||||
--wa-color-orange-80: #ffbb94 /* oklch(84.588% 0.09454 50.876) */;
|
||||
--wa-color-orange-70: #ff9266 /* oklch(76.744% 0.14429 42.309) */;
|
||||
--wa-color-orange-60: #f46a45 /* oklch(68.848% 0.17805 35.951) */;
|
||||
--wa-color-orange-50: #cd491c /* oklch(58.195% 0.17597 37.577) */;
|
||||
--wa-color-orange-40: #9f3501 /* oklch(47.889% 0.14981 39.957) */;
|
||||
--wa-color-orange-30: #802700 /* oklch(40.637% 0.1298 39.149) */;
|
||||
--wa-color-orange-20: #601b00 /* oklch(33.123% 0.10587 39.117) */;
|
||||
--wa-color-orange-10: #3c0d00 /* oklch(24.043% 0.07768 38.607) */;
|
||||
--wa-color-orange-05: #280600 /* oklch(18.644% 0.0607 38.252) */;
|
||||
--wa-color-orange: var(--wa-color-orange-60);
|
||||
--wa-color-orange-key: 60;
|
||||
--wa-color-orange-95: oklch(96.494% 0.0335 57.914);
|
||||
--wa-color-orange-90: oklch(92.556% 0.06963 56.631);
|
||||
--wa-color-orange-80: oklch(84.494% 0.12276 53.381);
|
||||
--wa-color-orange-70: oklch(76.375% 0.17194 46.091);
|
||||
--wa-color-orange-60: #eb713f /* oklch(68.398% 0.16422 41.446) */;
|
||||
--wa-color-orange-50: #cb4b1d /* oklch(58.153% 0.17174 38.404) */;
|
||||
--wa-color-orange-40: #a2310c /* oklch(48.028% 0.15488 36.538) */;
|
||||
--wa-color-orange-30: #7f2810 /* oklch(40.591% 0.12506 35.663) */;
|
||||
--wa-color-orange-20: #5d1d0e /* oklch(32.908% 0.09683 34.387) */;
|
||||
--wa-color-orange-10: #3a1005 /* oklch(24.088% 0.06954 35.613) */;
|
||||
--wa-color-orange-05: #270803 /* oklch(18.801% 0.05509 34.149) */;
|
||||
--wa-color-orange: var(--wa-color-orange-70);
|
||||
--wa-color-orange-key: 70;
|
||||
|
||||
--wa-color-yellow-95: #fef3cd /* oklch(96.322% 0.05069 93.748) */;
|
||||
--wa-color-yellow-90: #ffe495 /* oklch(92.377% 0.10246 91.296) */;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
@import url('base.css');
|
||||
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
@@ -18,17 +16,17 @@
|
||||
--wa-color-red: var(--wa-color-red-40);
|
||||
--wa-color-red-key: 40;
|
||||
|
||||
--wa-color-orange-95: #ffefe1 /* oklch(96.105% 0.02545 63.746) */;
|
||||
--wa-color-orange-90: #fbe0cb /* oklch(92.334% 0.04096 60.142) */;
|
||||
--wa-color-orange-80: #efc19f /* oklch(84.313% 0.06973 58.09) */;
|
||||
--wa-color-orange-70: #e1a173 /* oklch(76.013% 0.09772 56.368) */;
|
||||
--wa-color-orange-60: #d1824d /* oklch(67.937% 0.11938 53.195) */;
|
||||
--wa-color-orange-50: #b65d22 /* oklch(57.543% 0.13444 49.914) */;
|
||||
--wa-color-orange-40: #934107 /* oklch(47.603% 0.12628 47.819) */;
|
||||
--wa-color-orange-30: #773001 /* oklch(40.317% 0.11198 46.326) */;
|
||||
--wa-color-orange-20: #592100 /* oklch(32.692% 0.09239 45.689) */;
|
||||
--wa-color-orange-10: #381200 /* oklch(24.015% 0.06777 45.771) */;
|
||||
--wa-color-orange-05: #260900 /* oklch(18.789% 0.05427 44.405) */;
|
||||
--wa-color-orange-95: oklch(96.238% 0.02664 61.788);
|
||||
--wa-color-orange-90: #fbe1cc /* oklch(92.512% 0.04019 60.45) */;
|
||||
--wa-color-orange-80: #edc1a0 /* oklch(84.097% 0.06661 58.236) */;
|
||||
--wa-color-orange-70: #dda178 /* oklch(75.734% 0.09064 55.123) */;
|
||||
--wa-color-orange-60: #cd8351 /* oklch(67.646% 0.1134 53.172) */;
|
||||
--wa-color-orange-50: #b65d22 /* oklch(57.437% 0.13446 49.881) */;
|
||||
--wa-color-orange-40: oklch(47.576% 0.13426 46.452);
|
||||
--wa-color-orange-30: oklch(40.382% 0.12087 47.003);
|
||||
--wa-color-orange-20: oklch(32.846% 0.0965 46.227);
|
||||
--wa-color-orange-10: oklch(24.06% 0.06873 45.849);
|
||||
--wa-color-orange-05: #260900 /* oklch(18.727% 0.05359 44.791) */;
|
||||
--wa-color-orange: var(--wa-color-orange-50);
|
||||
--wa-color-orange-key: 50;
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
@import url('base.css');
|
||||
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
@@ -18,19 +16,19 @@
|
||||
--wa-color-red: var(--wa-color-red-40);
|
||||
--wa-color-red-key: 40;
|
||||
|
||||
--wa-color-orange-95: #fff0e8 /* oklch(96.479% 0.01949 50.157) */;
|
||||
--wa-color-orange-90: #ffdfc8 /* oklch(92.42% 0.04692 57.822) */;
|
||||
--wa-color-orange-80: #ffbb84 /* oklch(84.3% 0.10585 59.641) */;
|
||||
--wa-color-orange-70: #e79f64 /* oklch(76.075% 0.1143 59.207) */;
|
||||
--wa-color-orange-60: #ca854c /* oklch(67.612% 0.11209 58.789) */;
|
||||
--wa-color-orange-50: #a56732 /* oklch(57.133% 0.10488 58.75) */;
|
||||
--wa-color-orange-40: #824c1a /* oklch(47.091% 0.09621 59.168) */;
|
||||
--wa-color-orange-30: #6a3907 /* oklch(39.832% 0.09012 58.688) */;
|
||||
--wa-color-orange-20: #512800 /* oklch(32.57% 0.07814 57.787) */;
|
||||
--wa-color-orange-10: #331600 /* oklch(23.846% 0.05834 56.02) */;
|
||||
--wa-color-orange-05: #220c00 /* oklch(18.585% 0.04625 54.588) */;
|
||||
--wa-color-orange: var(--wa-color-orange-70);
|
||||
--wa-color-orange-key: 70;
|
||||
--wa-color-orange-95: oklch(96.126% 0.05417 66.333);
|
||||
--wa-color-orange-90: oklch(92.413% 0.07898 62.545);
|
||||
--wa-color-orange-80: #f9bd86 /* oklch(84.088% 0.09891 63.847) */;
|
||||
--wa-color-orange-70: #e2a05f /* oklch(75.707% 0.11352 64.057) */;
|
||||
--wa-color-orange-60: #d18228 /* oklch(67.572% 0.13809 64.146) */;
|
||||
--wa-color-orange-50: oklch(57.202% 0.13583 64.309);
|
||||
--wa-color-orange-40: oklch(47.462% 0.13789 64.939);
|
||||
--wa-color-orange-30: oklch(40.049% 0.12025 65.207);
|
||||
--wa-color-orange-20: oklch(32.552% 0.09777 64.859);
|
||||
--wa-color-orange-10: oklch(23.884% 0.07141 64.246);
|
||||
--wa-color-orange-05: oklch(18.698% 0.05597 65.589);
|
||||
--wa-color-orange: var(--wa-color-orange-60);
|
||||
--wa-color-orange-key: 60;
|
||||
|
||||
--wa-color-yellow-95: #fff4b3 /* oklch(96.064% 0.08319 99.657) */;
|
||||
--wa-color-yellow-90: #fee58c /* oklch(92.346% 0.11242 94.205) */;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
@import url('base.css');
|
||||
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
@@ -18,17 +16,17 @@
|
||||
--wa-color-red: var(--wa-color-red-50);
|
||||
--wa-color-red-key: 50;
|
||||
|
||||
--wa-color-orange-95: #f9f2ed /* oklch(96.52% 0.01008 58.217) */;
|
||||
--wa-color-orange-90: #f4e3d6 /* oklch(92.595% 0.02556 59.903) */;
|
||||
--wa-color-orange-80: #e6c3aa /* oklch(84.087% 0.05242 57.474) */;
|
||||
--wa-color-orange-70: #d3a685 /* oklch(75.824% 0.06976 57.703) */;
|
||||
--wa-color-orange-60: #bf8962 /* oklch(67.428% 0.08515 56.553) */;
|
||||
--wa-color-orange-50: #a26839 /* oklch(57.025% 0.09733 58.071) */;
|
||||
--wa-color-orange-40: #824b20 /* oklch(46.959% 0.09366 55.634) */;
|
||||
--wa-color-orange-30: #683a17 /* oklch(39.855% 0.08077 54.814) */;
|
||||
--wa-color-orange-20: #4d2a0e /* oklch(32.469% 0.06563 55.984) */;
|
||||
--wa-color-orange-10: #301705 /* oklch(23.621% 0.04968 54.306) */;
|
||||
--wa-color-orange-05: #1f0e03 /* oklch(18.572% 0.03671 56.067) */;
|
||||
--wa-color-orange-95: #f8f0ec /* oklch(96.084% 0.01043 54.557) */;
|
||||
--wa-color-orange-90: #f2e3d8 /* oklch(92.485% 0.02211 56.694) */;
|
||||
--wa-color-orange-80: #e4c4ad /* oklch(84.166% 0.04799 57.553) */;
|
||||
--wa-color-orange-70: #d3a583 /* oklch(75.674% 0.07148 57.481) */;
|
||||
--wa-color-orange-60: #bc8a65 /* oklch(67.319% 0.08062 57.054) */;
|
||||
--wa-color-orange-50: #9e6940 /* oklch(56.757% 0.08845 56.746) */;
|
||||
--wa-color-orange-40: #7e4d27 /* oklch(46.949% 0.08447 57.382) */;
|
||||
--wa-color-orange-30: #673a17 /* oklch(39.774% 0.0793 55.768) */;
|
||||
--wa-color-orange-20: #4f2906 /* oklch(32.45% 0.0725 57.629) */;
|
||||
--wa-color-orange-10: #311702 /* oklch(23.759% 0.05361 57.126) */;
|
||||
--wa-color-orange-05: #200d01 /* oklch(18.517% 0.04211 57.178) */;
|
||||
--wa-color-orange: var(--wa-color-orange-50);
|
||||
--wa-color-orange-key: 50;
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
@import url('base.css');
|
||||
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
@@ -18,19 +16,19 @@
|
||||
--wa-color-red: var(--wa-color-red-50);
|
||||
--wa-color-red-key: 50;
|
||||
|
||||
--wa-color-orange-95: #fff1e4 /* oklch(96.567% 0.02297 65.466) */;
|
||||
--wa-color-orange-90: #ffdfc4 /* oklch(92.327% 0.0504 62.301) */;
|
||||
--wa-color-orange-80: #ffbc80 /* oklch(84.418% 0.10871 62.363) */;
|
||||
--wa-color-orange-70: #ff9427 /* oklch(76.467% 0.1689 59.353) */;
|
||||
--wa-color-orange-60: #f16e00 /* oklch(68.563% 0.18516 48.551) */;
|
||||
--wa-color-orange-50: #c75003 /* oklch(58.025% 0.16741 44.08) */;
|
||||
--wa-color-orange-40: #9e3702 /* oklch(47.974% 0.14693 40.809) */;
|
||||
--wa-color-orange-30: #802700 /* oklch(40.637% 0.1298 39.149) */;
|
||||
--wa-color-orange-20: #601a00 /* oklch(32.994% 0.10679 38.524) */;
|
||||
--wa-color-orange-10: #3d0d00 /* oklch(24.253% 0.07888 38.298) */;
|
||||
--wa-color-orange-05: #290600 /* oklch(18.868% 0.06197 37.848) */;
|
||||
--wa-color-orange: var(--wa-color-orange-60);
|
||||
--wa-color-orange-key: 60;
|
||||
--wa-color-orange-95: oklch(96.488% 0.04965 58.92);
|
||||
--wa-color-orange-90: oklch(92.244% 0.08759 57.789);
|
||||
--wa-color-orange-80: oklch(84.32% 0.12702 56.232);
|
||||
--wa-color-orange-70: oklch(76.31% 0.17967 50.95);
|
||||
--wa-color-orange-60: #e87431 /* oklch(68.352% 0.16354 47.083) */;
|
||||
--wa-color-orange-50: oklch(58.23% 0.17947 43.3);
|
||||
--wa-color-orange-40: oklch(48.089% 0.16071 40.798);
|
||||
--wa-color-orange-30: oklch(40.708% 0.13699 39.616);
|
||||
--wa-color-orange-20: oklch(33.124% 0.1121 39.599);
|
||||
--wa-color-orange-10: oklch(24.257% 0.08204 39.602);
|
||||
--wa-color-orange-05: oklch(18.966% 0.06414 39.606);
|
||||
--wa-color-orange: var(--wa-color-orange-70);
|
||||
--wa-color-orange-key: 70;
|
||||
|
||||
--wa-color-yellow-95: #fff5b4 /* oklch(96.281% 0.08306 100.4) */;
|
||||
--wa-color-yellow-90: #fde572 /* oklch(91.915% 0.13738 97.724) */;
|
||||
|
||||
@@ -8,9 +8,7 @@ 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') && !file.endsWith('base.css'));
|
||||
export const paletteFiles = fs.readdirSync(PALETTE_DIR + '/').filter(file => file.endsWith('.css'));
|
||||
export const declarationRegex =
|
||||
/^\s*--wa-color-(?<hue>[a-z]+)(?:-(?<level>[0-9]+|key))?:\s*(?<color>.+?)\s*(\/\*.+?\*\/)?\s*;$/gm;
|
||||
export const rawCSS = {};
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
@import url('base.css');
|
||||
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
@@ -18,19 +16,19 @@
|
||||
--wa-color-red: var(--wa-color-red-50);
|
||||
--wa-color-red-key: 50;
|
||||
|
||||
--wa-color-orange-95: #fff1de /* oklch(96.415% 0.02927 75.692) */;
|
||||
--wa-color-orange-90: #fee0bc /* oklch(92.25% 0.05764 72.084) */;
|
||||
--wa-color-orange-80: #fdbe7a /* oklch(84.485% 0.11179 67.463) */;
|
||||
--wa-color-orange-70: #fb9641 /* oklch(76.472% 0.15504 57.097) */;
|
||||
--wa-color-orange-60: #f56b11 /* oklch(68.77% 0.18951 45.673) */;
|
||||
--wa-color-orange-50: #cf480e /* oklch(58.335% 0.18077 39.019) */;
|
||||
--wa-color-orange-40: #a13308 /* oklch(48.025% 0.15268 37.837) */;
|
||||
--wa-color-orange-30: #802604 /* oklch(40.538% 0.12998 37.701) */;
|
||||
--wa-color-orange-20: #601a02 /* oklch(33.012% 0.10627 37.613) */;
|
||||
--wa-color-orange-10: #3d0d01 /* oklch(24.269% 0.07841 37.157) */;
|
||||
--wa-color-orange-05: #290600 /* oklch(18.868% 0.06197 37.848) */;
|
||||
--wa-color-orange: var(--wa-color-orange-60);
|
||||
--wa-color-orange-key: 60;
|
||||
--wa-color-orange-95: oklch(96.355% 0.05982 62.448);
|
||||
--wa-color-orange-90: oklch(92.371% 0.10134 60.314);
|
||||
--wa-color-orange-80: oklch(84.228% 0.13101 54.157);
|
||||
--wa-color-orange-70: oklch(76.275% 0.16839 51.143);
|
||||
--wa-color-orange-60: #e67530 /* oklch(68.22% 0.16179 47.997) */;
|
||||
--wa-color-orange-50: oklch(58.011% 0.16819 44.953);
|
||||
--wa-color-orange-40: #9b390d /* oklch(47.739% 0.14004 40.585) */;
|
||||
--wa-color-orange-30: #7a2b17 /* oklch(40.323% 0.11475 35.602) */;
|
||||
--wa-color-orange-20: #5c1e0f /* oklch(32.963% 0.09552 34.666) */;
|
||||
--wa-color-orange-10: #3a0f06 /* oklch(24.042% 0.07066 34.715) */;
|
||||
--wa-color-orange-05: #270803 /* oklch(18.867% 0.05444 34.696) */;
|
||||
--wa-color-orange: var(--wa-color-orange-70);
|
||||
--wa-color-orange-key: 70;
|
||||
|
||||
--wa-color-yellow-95: #fef6ab /* oklch(96.234% 0.09455 102.83) */;
|
||||
--wa-color-yellow-90: #fde761 /* oklch(92.138% 0.15325 99.997) */;
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
:where([class^='wa-palette-'], [class*=' wa-palette-']),
|
||||
.wa-danger-blue {
|
||||
--wa-color-danger-95: var(--wa-color-blue-95);
|
||||
--wa-color-danger-90: var(--wa-color-blue-90);
|
||||
--wa-color-danger-80: var(--wa-color-blue-80);
|
||||
--wa-color-danger-70: var(--wa-color-blue-70);
|
||||
--wa-color-danger-60: var(--wa-color-blue-60);
|
||||
--wa-color-danger-50: var(--wa-color-blue-50);
|
||||
--wa-color-danger-40: var(--wa-color-blue-40);
|
||||
--wa-color-danger-30: var(--wa-color-blue-30);
|
||||
--wa-color-danger-20: var(--wa-color-blue-20);
|
||||
--wa-color-danger-10: var(--wa-color-blue-10);
|
||||
--wa-color-danger-05: var(--wa-color-blue-05);
|
||||
--wa-color-danger: var(--wa-color-blue);
|
||||
|
||||
--wa-color-danger-lt-50: var(--wa-color-blue-lt-50);
|
||||
--wa-color-danger-gte-50: var(--wa-color-blue-gte-50);
|
||||
|
||||
--wa-color-danger-lt-60: var(--wa-color-blue-lt-60);
|
||||
--wa-color-danger-gte-60: var(--wa-color-blue-gte-60);
|
||||
|
||||
--wa-color-danger-lt-70: var(--wa-color-blue-lt-70);
|
||||
--wa-color-danger-gte-70: var(--wa-color-blue-gte-70);
|
||||
|
||||
--wa-color-danger-max-50: var(--wa-color-blue-max-50);
|
||||
--wa-color-danger-min-50: var(--wa-color-blue-min-50);
|
||||
--wa-color-danger-max-60: var(--wa-color-blue-max-60);
|
||||
--wa-color-danger-min-60: var(--wa-color-blue-min-60);
|
||||
--wa-color-danger-max-70: var(--wa-color-blue-max-70);
|
||||
--wa-color-danger-min-70: var(--wa-color-blue-min-70);
|
||||
|
||||
--wa-color-danger-on: var(--wa-color-blue-on);
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
:where([class^='wa-palette-'], [class*=' wa-palette-']),
|
||||
.wa-danger-cyan {
|
||||
--wa-color-danger-95: var(--wa-color-cyan-95);
|
||||
--wa-color-danger-90: var(--wa-color-cyan-90);
|
||||
--wa-color-danger-80: var(--wa-color-cyan-80);
|
||||
--wa-color-danger-70: var(--wa-color-cyan-70);
|
||||
--wa-color-danger-60: var(--wa-color-cyan-60);
|
||||
--wa-color-danger-50: var(--wa-color-cyan-50);
|
||||
--wa-color-danger-40: var(--wa-color-cyan-40);
|
||||
--wa-color-danger-30: var(--wa-color-cyan-30);
|
||||
--wa-color-danger-20: var(--wa-color-cyan-20);
|
||||
--wa-color-danger-10: var(--wa-color-cyan-10);
|
||||
--wa-color-danger-05: var(--wa-color-cyan-05);
|
||||
--wa-color-danger: var(--wa-color-cyan);
|
||||
|
||||
--wa-color-danger-lt-50: var(--wa-color-cyan-lt-50);
|
||||
--wa-color-danger-gte-50: var(--wa-color-cyan-gte-50);
|
||||
|
||||
--wa-color-danger-lt-60: var(--wa-color-cyan-lt-60);
|
||||
--wa-color-danger-gte-60: var(--wa-color-cyan-gte-60);
|
||||
|
||||
--wa-color-danger-lt-70: var(--wa-color-cyan-lt-70);
|
||||
--wa-color-danger-gte-70: var(--wa-color-cyan-gte-70);
|
||||
|
||||
--wa-color-danger-max-50: var(--wa-color-cyan-max-50);
|
||||
--wa-color-danger-min-50: var(--wa-color-cyan-min-50);
|
||||
--wa-color-danger-max-60: var(--wa-color-cyan-max-60);
|
||||
--wa-color-danger-min-60: var(--wa-color-cyan-min-60);
|
||||
--wa-color-danger-max-70: var(--wa-color-cyan-max-70);
|
||||
--wa-color-danger-min-70: var(--wa-color-cyan-min-70);
|
||||
|
||||
--wa-color-danger-on: var(--wa-color-cyan-on);
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
:where([class^='wa-palette-'], [class*=' wa-palette-']),
|
||||
.wa-danger-gray {
|
||||
--wa-color-danger-95: var(--wa-color-gray-95);
|
||||
--wa-color-danger-90: var(--wa-color-gray-90);
|
||||
--wa-color-danger-80: var(--wa-color-gray-80);
|
||||
--wa-color-danger-70: var(--wa-color-gray-70);
|
||||
--wa-color-danger-60: var(--wa-color-gray-60);
|
||||
--wa-color-danger-50: var(--wa-color-gray-50);
|
||||
--wa-color-danger-40: var(--wa-color-gray-40);
|
||||
--wa-color-danger-30: var(--wa-color-gray-30);
|
||||
--wa-color-danger-20: var(--wa-color-gray-20);
|
||||
--wa-color-danger-10: var(--wa-color-gray-10);
|
||||
--wa-color-danger-05: var(--wa-color-gray-05);
|
||||
--wa-color-danger: var(--wa-color-gray);
|
||||
|
||||
--wa-color-danger-lt-50: var(--wa-color-gray-lt-50);
|
||||
--wa-color-danger-gte-50: var(--wa-color-gray-gte-50);
|
||||
|
||||
--wa-color-danger-lt-60: var(--wa-color-gray-lt-60);
|
||||
--wa-color-danger-gte-60: var(--wa-color-gray-gte-60);
|
||||
|
||||
--wa-color-danger-lt-70: var(--wa-color-gray-lt-70);
|
||||
--wa-color-danger-gte-70: var(--wa-color-gray-gte-70);
|
||||
|
||||
--wa-color-danger-max-50: var(--wa-color-gray-max-50);
|
||||
--wa-color-danger-min-50: var(--wa-color-gray-min-50);
|
||||
--wa-color-danger-max-60: var(--wa-color-gray-max-60);
|
||||
--wa-color-danger-min-60: var(--wa-color-gray-min-60);
|
||||
--wa-color-danger-max-70: var(--wa-color-gray-max-70);
|
||||
--wa-color-danger-min-70: var(--wa-color-gray-min-70);
|
||||
|
||||
--wa-color-danger-on: var(--wa-color-gray-on);
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
:where([class^='wa-palette-'], [class*=' wa-palette-']),
|
||||
.wa-danger-green {
|
||||
--wa-color-danger-95: var(--wa-color-green-95);
|
||||
--wa-color-danger-90: var(--wa-color-green-90);
|
||||
--wa-color-danger-80: var(--wa-color-green-80);
|
||||
--wa-color-danger-70: var(--wa-color-green-70);
|
||||
--wa-color-danger-60: var(--wa-color-green-60);
|
||||
--wa-color-danger-50: var(--wa-color-green-50);
|
||||
--wa-color-danger-40: var(--wa-color-green-40);
|
||||
--wa-color-danger-30: var(--wa-color-green-30);
|
||||
--wa-color-danger-20: var(--wa-color-green-20);
|
||||
--wa-color-danger-10: var(--wa-color-green-10);
|
||||
--wa-color-danger-05: var(--wa-color-green-05);
|
||||
--wa-color-danger: var(--wa-color-green);
|
||||
|
||||
--wa-color-danger-lt-50: var(--wa-color-green-lt-50);
|
||||
--wa-color-danger-gte-50: var(--wa-color-green-gte-50);
|
||||
|
||||
--wa-color-danger-lt-60: var(--wa-color-green-lt-60);
|
||||
--wa-color-danger-gte-60: var(--wa-color-green-gte-60);
|
||||
|
||||
--wa-color-danger-lt-70: var(--wa-color-green-lt-70);
|
||||
--wa-color-danger-gte-70: var(--wa-color-green-gte-70);
|
||||
|
||||
--wa-color-danger-max-50: var(--wa-color-green-max-50);
|
||||
--wa-color-danger-min-50: var(--wa-color-green-min-50);
|
||||
--wa-color-danger-max-60: var(--wa-color-green-max-60);
|
||||
--wa-color-danger-min-60: var(--wa-color-green-min-60);
|
||||
--wa-color-danger-max-70: var(--wa-color-green-max-70);
|
||||
--wa-color-danger-min-70: var(--wa-color-green-min-70);
|
||||
|
||||
--wa-color-danger-on: var(--wa-color-green-on);
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
:where([class^='wa-palette-'], [class*=' wa-palette-']),
|
||||
.wa-danger-indigo {
|
||||
--wa-color-danger-95: var(--wa-color-indigo-95);
|
||||
--wa-color-danger-90: var(--wa-color-indigo-90);
|
||||
--wa-color-danger-80: var(--wa-color-indigo-80);
|
||||
--wa-color-danger-70: var(--wa-color-indigo-70);
|
||||
--wa-color-danger-60: var(--wa-color-indigo-60);
|
||||
--wa-color-danger-50: var(--wa-color-indigo-50);
|
||||
--wa-color-danger-40: var(--wa-color-indigo-40);
|
||||
--wa-color-danger-30: var(--wa-color-indigo-30);
|
||||
--wa-color-danger-20: var(--wa-color-indigo-20);
|
||||
--wa-color-danger-10: var(--wa-color-indigo-10);
|
||||
--wa-color-danger-05: var(--wa-color-indigo-05);
|
||||
--wa-color-danger: var(--wa-color-indigo);
|
||||
|
||||
--wa-color-danger-lt-50: var(--wa-color-indigo-lt-50);
|
||||
--wa-color-danger-gte-50: var(--wa-color-indigo-gte-50);
|
||||
|
||||
--wa-color-danger-lt-60: var(--wa-color-indigo-lt-60);
|
||||
--wa-color-danger-gte-60: var(--wa-color-indigo-gte-60);
|
||||
|
||||
--wa-color-danger-lt-70: var(--wa-color-indigo-lt-70);
|
||||
--wa-color-danger-gte-70: var(--wa-color-indigo-gte-70);
|
||||
|
||||
--wa-color-danger-max-50: var(--wa-color-indigo-max-50);
|
||||
--wa-color-danger-min-50: var(--wa-color-indigo-min-50);
|
||||
--wa-color-danger-max-60: var(--wa-color-indigo-max-60);
|
||||
--wa-color-danger-min-60: var(--wa-color-indigo-min-60);
|
||||
--wa-color-danger-max-70: var(--wa-color-indigo-max-70);
|
||||
--wa-color-danger-min-70: var(--wa-color-indigo-min-70);
|
||||
|
||||
--wa-color-danger-on: var(--wa-color-indigo-on);
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
:where([class^='wa-palette-'], [class*=' wa-palette-']),
|
||||
.wa-danger-orange {
|
||||
--wa-color-danger-95: var(--wa-color-orange-95);
|
||||
--wa-color-danger-90: var(--wa-color-orange-90);
|
||||
--wa-color-danger-80: var(--wa-color-orange-80);
|
||||
--wa-color-danger-70: var(--wa-color-orange-70);
|
||||
--wa-color-danger-60: var(--wa-color-orange-60);
|
||||
--wa-color-danger-50: var(--wa-color-orange-50);
|
||||
--wa-color-danger-40: var(--wa-color-orange-40);
|
||||
--wa-color-danger-30: var(--wa-color-orange-30);
|
||||
--wa-color-danger-20: var(--wa-color-orange-20);
|
||||
--wa-color-danger-10: var(--wa-color-orange-10);
|
||||
--wa-color-danger-05: var(--wa-color-orange-05);
|
||||
--wa-color-danger: var(--wa-color-orange);
|
||||
|
||||
--wa-color-danger-lt-50: var(--wa-color-orange-lt-50);
|
||||
--wa-color-danger-gte-50: var(--wa-color-orange-gte-50);
|
||||
|
||||
--wa-color-danger-lt-60: var(--wa-color-orange-lt-60);
|
||||
--wa-color-danger-gte-60: var(--wa-color-orange-gte-60);
|
||||
|
||||
--wa-color-danger-lt-70: var(--wa-color-orange-lt-70);
|
||||
--wa-color-danger-gte-70: var(--wa-color-orange-gte-70);
|
||||
|
||||
--wa-color-danger-max-50: var(--wa-color-orange-max-50);
|
||||
--wa-color-danger-min-50: var(--wa-color-orange-min-50);
|
||||
--wa-color-danger-max-60: var(--wa-color-orange-max-60);
|
||||
--wa-color-danger-min-60: var(--wa-color-orange-min-60);
|
||||
--wa-color-danger-max-70: var(--wa-color-orange-max-70);
|
||||
--wa-color-danger-min-70: var(--wa-color-orange-min-70);
|
||||
|
||||
--wa-color-danger-on: var(--wa-color-orange-on);
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
:where([class^='wa-palette-'], [class*=' wa-palette-']),
|
||||
.wa-danger-pink {
|
||||
--wa-color-danger-95: var(--wa-color-pink-95);
|
||||
--wa-color-danger-90: var(--wa-color-pink-90);
|
||||
--wa-color-danger-80: var(--wa-color-pink-80);
|
||||
--wa-color-danger-70: var(--wa-color-pink-70);
|
||||
--wa-color-danger-60: var(--wa-color-pink-60);
|
||||
--wa-color-danger-50: var(--wa-color-pink-50);
|
||||
--wa-color-danger-40: var(--wa-color-pink-40);
|
||||
--wa-color-danger-30: var(--wa-color-pink-30);
|
||||
--wa-color-danger-20: var(--wa-color-pink-20);
|
||||
--wa-color-danger-10: var(--wa-color-pink-10);
|
||||
--wa-color-danger-05: var(--wa-color-pink-05);
|
||||
--wa-color-danger: var(--wa-color-pink);
|
||||
|
||||
--wa-color-danger-lt-50: var(--wa-color-pink-lt-50);
|
||||
--wa-color-danger-gte-50: var(--wa-color-pink-gte-50);
|
||||
|
||||
--wa-color-danger-lt-60: var(--wa-color-pink-lt-60);
|
||||
--wa-color-danger-gte-60: var(--wa-color-pink-gte-60);
|
||||
|
||||
--wa-color-danger-lt-70: var(--wa-color-pink-lt-70);
|
||||
--wa-color-danger-gte-70: var(--wa-color-pink-gte-70);
|
||||
|
||||
--wa-color-danger-max-50: var(--wa-color-pink-max-50);
|
||||
--wa-color-danger-min-50: var(--wa-color-pink-min-50);
|
||||
--wa-color-danger-max-60: var(--wa-color-pink-max-60);
|
||||
--wa-color-danger-min-60: var(--wa-color-pink-min-60);
|
||||
--wa-color-danger-max-70: var(--wa-color-pink-max-70);
|
||||
--wa-color-danger-min-70: var(--wa-color-pink-min-70);
|
||||
|
||||
--wa-color-danger-on: var(--wa-color-pink-on);
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
:where([class^='wa-palette-'], [class*=' wa-palette-']),
|
||||
.wa-danger-purple {
|
||||
--wa-color-danger-95: var(--wa-color-purple-95);
|
||||
--wa-color-danger-90: var(--wa-color-purple-90);
|
||||
--wa-color-danger-80: var(--wa-color-purple-80);
|
||||
--wa-color-danger-70: var(--wa-color-purple-70);
|
||||
--wa-color-danger-60: var(--wa-color-purple-60);
|
||||
--wa-color-danger-50: var(--wa-color-purple-50);
|
||||
--wa-color-danger-40: var(--wa-color-purple-40);
|
||||
--wa-color-danger-30: var(--wa-color-purple-30);
|
||||
--wa-color-danger-20: var(--wa-color-purple-20);
|
||||
--wa-color-danger-10: var(--wa-color-purple-10);
|
||||
--wa-color-danger-05: var(--wa-color-purple-05);
|
||||
--wa-color-danger: var(--wa-color-purple);
|
||||
|
||||
--wa-color-danger-lt-50: var(--wa-color-purple-lt-50);
|
||||
--wa-color-danger-gte-50: var(--wa-color-purple-gte-50);
|
||||
|
||||
--wa-color-danger-lt-60: var(--wa-color-purple-lt-60);
|
||||
--wa-color-danger-gte-60: var(--wa-color-purple-gte-60);
|
||||
|
||||
--wa-color-danger-lt-70: var(--wa-color-purple-lt-70);
|
||||
--wa-color-danger-gte-70: var(--wa-color-purple-gte-70);
|
||||
|
||||
--wa-color-danger-max-50: var(--wa-color-purple-max-50);
|
||||
--wa-color-danger-min-50: var(--wa-color-purple-min-50);
|
||||
--wa-color-danger-max-60: var(--wa-color-purple-max-60);
|
||||
--wa-color-danger-min-60: var(--wa-color-purple-min-60);
|
||||
--wa-color-danger-max-70: var(--wa-color-purple-max-70);
|
||||
--wa-color-danger-min-70: var(--wa-color-purple-min-70);
|
||||
|
||||
--wa-color-danger-on: var(--wa-color-purple-on);
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
:where([class^='wa-palette-'], [class*=' wa-palette-']),
|
||||
.wa-danger-red {
|
||||
--wa-color-danger-95: var(--wa-color-red-95);
|
||||
--wa-color-danger-90: var(--wa-color-red-90);
|
||||
--wa-color-danger-80: var(--wa-color-red-80);
|
||||
--wa-color-danger-70: var(--wa-color-red-70);
|
||||
--wa-color-danger-60: var(--wa-color-red-60);
|
||||
--wa-color-danger-50: var(--wa-color-red-50);
|
||||
--wa-color-danger-40: var(--wa-color-red-40);
|
||||
--wa-color-danger-30: var(--wa-color-red-30);
|
||||
--wa-color-danger-20: var(--wa-color-red-20);
|
||||
--wa-color-danger-10: var(--wa-color-red-10);
|
||||
--wa-color-danger-05: var(--wa-color-red-05);
|
||||
--wa-color-danger: var(--wa-color-red);
|
||||
|
||||
--wa-color-danger-lt-50: var(--wa-color-red-lt-50);
|
||||
--wa-color-danger-gte-50: var(--wa-color-red-gte-50);
|
||||
|
||||
--wa-color-danger-lt-60: var(--wa-color-red-lt-60);
|
||||
--wa-color-danger-gte-60: var(--wa-color-red-gte-60);
|
||||
|
||||
--wa-color-danger-lt-70: var(--wa-color-red-lt-70);
|
||||
--wa-color-danger-gte-70: var(--wa-color-red-gte-70);
|
||||
|
||||
--wa-color-danger-max-50: var(--wa-color-red-max-50);
|
||||
--wa-color-danger-min-50: var(--wa-color-red-min-50);
|
||||
--wa-color-danger-max-60: var(--wa-color-red-max-60);
|
||||
--wa-color-danger-min-60: var(--wa-color-red-min-60);
|
||||
--wa-color-danger-max-70: var(--wa-color-red-max-70);
|
||||
--wa-color-danger-min-70: var(--wa-color-red-min-70);
|
||||
|
||||
--wa-color-danger-on: var(--wa-color-red-on);
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
:where([class^='wa-palette-'], [class*=' wa-palette-']),
|
||||
.wa-danger-yellow {
|
||||
--wa-color-danger-95: var(--wa-color-yellow-95);
|
||||
--wa-color-danger-90: var(--wa-color-yellow-90);
|
||||
--wa-color-danger-80: var(--wa-color-yellow-80);
|
||||
--wa-color-danger-70: var(--wa-color-yellow-70);
|
||||
--wa-color-danger-60: var(--wa-color-yellow-60);
|
||||
--wa-color-danger-50: var(--wa-color-yellow-50);
|
||||
--wa-color-danger-40: var(--wa-color-yellow-40);
|
||||
--wa-color-danger-30: var(--wa-color-yellow-30);
|
||||
--wa-color-danger-20: var(--wa-color-yellow-20);
|
||||
--wa-color-danger-10: var(--wa-color-yellow-10);
|
||||
--wa-color-danger-05: var(--wa-color-yellow-05);
|
||||
--wa-color-danger: var(--wa-color-yellow);
|
||||
|
||||
--wa-color-danger-lt-50: var(--wa-color-yellow-lt-50);
|
||||
--wa-color-danger-gte-50: var(--wa-color-yellow-gte-50);
|
||||
|
||||
--wa-color-danger-lt-60: var(--wa-color-yellow-lt-60);
|
||||
--wa-color-danger-gte-60: var(--wa-color-yellow-gte-60);
|
||||
|
||||
--wa-color-danger-lt-70: var(--wa-color-yellow-lt-70);
|
||||
--wa-color-danger-gte-70: var(--wa-color-yellow-gte-70);
|
||||
|
||||
--wa-color-danger-max-50: var(--wa-color-yellow-max-50);
|
||||
--wa-color-danger-min-50: var(--wa-color-yellow-min-50);
|
||||
--wa-color-danger-max-60: var(--wa-color-yellow-max-60);
|
||||
--wa-color-danger-min-60: var(--wa-color-yellow-min-60);
|
||||
--wa-color-danger-max-70: var(--wa-color-yellow-max-70);
|
||||
--wa-color-danger-min-70: var(--wa-color-yellow-min-70);
|
||||
|
||||
--wa-color-danger-on: var(--wa-color-yellow-on);
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
:where([class^='wa-palette-'], [class*=' wa-palette-']),
|
||||
.wa-neutral-blue {
|
||||
--wa-color-neutral-95: var(--wa-color-blue-95);
|
||||
--wa-color-neutral-90: var(--wa-color-blue-90);
|
||||
--wa-color-neutral-80: var(--wa-color-blue-80);
|
||||
--wa-color-neutral-70: var(--wa-color-blue-70);
|
||||
--wa-color-neutral-60: var(--wa-color-blue-60);
|
||||
--wa-color-neutral-50: var(--wa-color-blue-50);
|
||||
--wa-color-neutral-40: var(--wa-color-blue-40);
|
||||
--wa-color-neutral-30: var(--wa-color-blue-30);
|
||||
--wa-color-neutral-20: var(--wa-color-blue-20);
|
||||
--wa-color-neutral-10: var(--wa-color-blue-10);
|
||||
--wa-color-neutral-05: var(--wa-color-blue-05);
|
||||
--wa-color-neutral: var(--wa-color-blue);
|
||||
|
||||
--wa-color-neutral-lt-50: var(--wa-color-blue-lt-50);
|
||||
--wa-color-neutral-gte-50: var(--wa-color-blue-gte-50);
|
||||
|
||||
--wa-color-neutral-lt-60: var(--wa-color-blue-lt-60);
|
||||
--wa-color-neutral-gte-60: var(--wa-color-blue-gte-60);
|
||||
|
||||
--wa-color-neutral-lt-70: var(--wa-color-blue-lt-70);
|
||||
--wa-color-neutral-gte-70: var(--wa-color-blue-gte-70);
|
||||
|
||||
--wa-color-neutral-max-50: var(--wa-color-blue-max-50);
|
||||
--wa-color-neutral-min-50: var(--wa-color-blue-min-50);
|
||||
--wa-color-neutral-max-60: var(--wa-color-blue-max-60);
|
||||
--wa-color-neutral-min-60: var(--wa-color-blue-min-60);
|
||||
--wa-color-neutral-max-70: var(--wa-color-blue-max-70);
|
||||
--wa-color-neutral-min-70: var(--wa-color-blue-min-70);
|
||||
|
||||
--wa-color-neutral-on: var(--wa-color-blue-on);
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
:where([class^='wa-palette-'], [class*=' wa-palette-']),
|
||||
.wa-neutral-cyan {
|
||||
--wa-color-neutral-95: var(--wa-color-cyan-95);
|
||||
--wa-color-neutral-90: var(--wa-color-cyan-90);
|
||||
--wa-color-neutral-80: var(--wa-color-cyan-80);
|
||||
--wa-color-neutral-70: var(--wa-color-cyan-70);
|
||||
--wa-color-neutral-60: var(--wa-color-cyan-60);
|
||||
--wa-color-neutral-50: var(--wa-color-cyan-50);
|
||||
--wa-color-neutral-40: var(--wa-color-cyan-40);
|
||||
--wa-color-neutral-30: var(--wa-color-cyan-30);
|
||||
--wa-color-neutral-20: var(--wa-color-cyan-20);
|
||||
--wa-color-neutral-10: var(--wa-color-cyan-10);
|
||||
--wa-color-neutral-05: var(--wa-color-cyan-05);
|
||||
--wa-color-neutral: var(--wa-color-cyan);
|
||||
|
||||
--wa-color-neutral-lt-50: var(--wa-color-cyan-lt-50);
|
||||
--wa-color-neutral-gte-50: var(--wa-color-cyan-gte-50);
|
||||
|
||||
--wa-color-neutral-lt-60: var(--wa-color-cyan-lt-60);
|
||||
--wa-color-neutral-gte-60: var(--wa-color-cyan-gte-60);
|
||||
|
||||
--wa-color-neutral-lt-70: var(--wa-color-cyan-lt-70);
|
||||
--wa-color-neutral-gte-70: var(--wa-color-cyan-gte-70);
|
||||
|
||||
--wa-color-neutral-max-50: var(--wa-color-cyan-max-50);
|
||||
--wa-color-neutral-min-50: var(--wa-color-cyan-min-50);
|
||||
--wa-color-neutral-max-60: var(--wa-color-cyan-max-60);
|
||||
--wa-color-neutral-min-60: var(--wa-color-cyan-min-60);
|
||||
--wa-color-neutral-max-70: var(--wa-color-cyan-max-70);
|
||||
--wa-color-neutral-min-70: var(--wa-color-cyan-min-70);
|
||||
|
||||
--wa-color-neutral-on: var(--wa-color-cyan-on);
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
:where([class^='wa-palette-'], [class*=' wa-palette-']),
|
||||
.wa-neutral-gray {
|
||||
--wa-color-neutral-95: var(--wa-color-gray-95);
|
||||
--wa-color-neutral-90: var(--wa-color-gray-90);
|
||||
--wa-color-neutral-80: var(--wa-color-gray-80);
|
||||
--wa-color-neutral-70: var(--wa-color-gray-70);
|
||||
--wa-color-neutral-60: var(--wa-color-gray-60);
|
||||
--wa-color-neutral-50: var(--wa-color-gray-50);
|
||||
--wa-color-neutral-40: var(--wa-color-gray-40);
|
||||
--wa-color-neutral-30: var(--wa-color-gray-30);
|
||||
--wa-color-neutral-20: var(--wa-color-gray-20);
|
||||
--wa-color-neutral-10: var(--wa-color-gray-10);
|
||||
--wa-color-neutral-05: var(--wa-color-gray-05);
|
||||
--wa-color-neutral: var(--wa-color-gray);
|
||||
|
||||
--wa-color-neutral-lt-50: var(--wa-color-gray-lt-50);
|
||||
--wa-color-neutral-gte-50: var(--wa-color-gray-gte-50);
|
||||
|
||||
--wa-color-neutral-lt-60: var(--wa-color-gray-lt-60);
|
||||
--wa-color-neutral-gte-60: var(--wa-color-gray-gte-60);
|
||||
|
||||
--wa-color-neutral-lt-70: var(--wa-color-gray-lt-70);
|
||||
--wa-color-neutral-gte-70: var(--wa-color-gray-gte-70);
|
||||
|
||||
--wa-color-neutral-max-50: var(--wa-color-gray-max-50);
|
||||
--wa-color-neutral-min-50: var(--wa-color-gray-min-50);
|
||||
--wa-color-neutral-max-60: var(--wa-color-gray-max-60);
|
||||
--wa-color-neutral-min-60: var(--wa-color-gray-min-60);
|
||||
--wa-color-neutral-max-70: var(--wa-color-gray-max-70);
|
||||
--wa-color-neutral-min-70: var(--wa-color-gray-min-70);
|
||||
|
||||
--wa-color-neutral-on: var(--wa-color-gray-on);
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
:where([class^='wa-palette-'], [class*=' wa-palette-']),
|
||||
.wa-neutral-green {
|
||||
--wa-color-neutral-95: var(--wa-color-green-95);
|
||||
--wa-color-neutral-90: var(--wa-color-green-90);
|
||||
--wa-color-neutral-80: var(--wa-color-green-80);
|
||||
--wa-color-neutral-70: var(--wa-color-green-70);
|
||||
--wa-color-neutral-60: var(--wa-color-green-60);
|
||||
--wa-color-neutral-50: var(--wa-color-green-50);
|
||||
--wa-color-neutral-40: var(--wa-color-green-40);
|
||||
--wa-color-neutral-30: var(--wa-color-green-30);
|
||||
--wa-color-neutral-20: var(--wa-color-green-20);
|
||||
--wa-color-neutral-10: var(--wa-color-green-10);
|
||||
--wa-color-neutral-05: var(--wa-color-green-05);
|
||||
--wa-color-neutral: var(--wa-color-green);
|
||||
|
||||
--wa-color-neutral-lt-50: var(--wa-color-green-lt-50);
|
||||
--wa-color-neutral-gte-50: var(--wa-color-green-gte-50);
|
||||
|
||||
--wa-color-neutral-lt-60: var(--wa-color-green-lt-60);
|
||||
--wa-color-neutral-gte-60: var(--wa-color-green-gte-60);
|
||||
|
||||
--wa-color-neutral-lt-70: var(--wa-color-green-lt-70);
|
||||
--wa-color-neutral-gte-70: var(--wa-color-green-gte-70);
|
||||
|
||||
--wa-color-neutral-max-50: var(--wa-color-green-max-50);
|
||||
--wa-color-neutral-min-50: var(--wa-color-green-min-50);
|
||||
--wa-color-neutral-max-60: var(--wa-color-green-max-60);
|
||||
--wa-color-neutral-min-60: var(--wa-color-green-min-60);
|
||||
--wa-color-neutral-max-70: var(--wa-color-green-max-70);
|
||||
--wa-color-neutral-min-70: var(--wa-color-green-min-70);
|
||||
|
||||
--wa-color-neutral-on: var(--wa-color-green-on);
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
:where([class^='wa-palette-'], [class*=' wa-palette-']),
|
||||
.wa-neutral-indigo {
|
||||
--wa-color-neutral-95: var(--wa-color-indigo-95);
|
||||
--wa-color-neutral-90: var(--wa-color-indigo-90);
|
||||
--wa-color-neutral-80: var(--wa-color-indigo-80);
|
||||
--wa-color-neutral-70: var(--wa-color-indigo-70);
|
||||
--wa-color-neutral-60: var(--wa-color-indigo-60);
|
||||
--wa-color-neutral-50: var(--wa-color-indigo-50);
|
||||
--wa-color-neutral-40: var(--wa-color-indigo-40);
|
||||
--wa-color-neutral-30: var(--wa-color-indigo-30);
|
||||
--wa-color-neutral-20: var(--wa-color-indigo-20);
|
||||
--wa-color-neutral-10: var(--wa-color-indigo-10);
|
||||
--wa-color-neutral-05: var(--wa-color-indigo-05);
|
||||
--wa-color-neutral: var(--wa-color-indigo);
|
||||
|
||||
--wa-color-neutral-lt-50: var(--wa-color-indigo-lt-50);
|
||||
--wa-color-neutral-gte-50: var(--wa-color-indigo-gte-50);
|
||||
|
||||
--wa-color-neutral-lt-60: var(--wa-color-indigo-lt-60);
|
||||
--wa-color-neutral-gte-60: var(--wa-color-indigo-gte-60);
|
||||
|
||||
--wa-color-neutral-lt-70: var(--wa-color-indigo-lt-70);
|
||||
--wa-color-neutral-gte-70: var(--wa-color-indigo-gte-70);
|
||||
|
||||
--wa-color-neutral-max-50: var(--wa-color-indigo-max-50);
|
||||
--wa-color-neutral-min-50: var(--wa-color-indigo-min-50);
|
||||
--wa-color-neutral-max-60: var(--wa-color-indigo-max-60);
|
||||
--wa-color-neutral-min-60: var(--wa-color-indigo-min-60);
|
||||
--wa-color-neutral-max-70: var(--wa-color-indigo-max-70);
|
||||
--wa-color-neutral-min-70: var(--wa-color-indigo-min-70);
|
||||
|
||||
--wa-color-neutral-on: var(--wa-color-indigo-on);
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
:where(:root),
|
||||
:host,
|
||||
:where([class^='wa-theme-'], [class*=' wa-theme-']),
|
||||
:where([class^='wa-palette-'], [class*=' wa-palette-']),
|
||||
.wa-neutral-orange {
|
||||
--wa-color-neutral-95: var(--wa-color-orange-95);
|
||||
--wa-color-neutral-90: var(--wa-color-orange-90);
|
||||
--wa-color-neutral-80: var(--wa-color-orange-80);
|
||||
--wa-color-neutral-70: var(--wa-color-orange-70);
|
||||
--wa-color-neutral-60: var(--wa-color-orange-60);
|
||||
--wa-color-neutral-50: var(--wa-color-orange-50);
|
||||
--wa-color-neutral-40: var(--wa-color-orange-40);
|
||||
--wa-color-neutral-30: var(--wa-color-orange-30);
|
||||
--wa-color-neutral-20: var(--wa-color-orange-20);
|
||||
--wa-color-neutral-10: var(--wa-color-orange-10);
|
||||
--wa-color-neutral-05: var(--wa-color-orange-05);
|
||||
--wa-color-neutral: var(--wa-color-orange);
|
||||
|
||||
--wa-color-neutral-lt-50: var(--wa-color-orange-lt-50);
|
||||
--wa-color-neutral-gte-50: var(--wa-color-orange-gte-50);
|
||||
|
||||
--wa-color-neutral-lt-60: var(--wa-color-orange-lt-60);
|
||||
--wa-color-neutral-gte-60: var(--wa-color-orange-gte-60);
|
||||
|
||||
--wa-color-neutral-lt-70: var(--wa-color-orange-lt-70);
|
||||
--wa-color-neutral-gte-70: var(--wa-color-orange-gte-70);
|
||||
|
||||
--wa-color-neutral-max-50: var(--wa-color-orange-max-50);
|
||||
--wa-color-neutral-min-50: var(--wa-color-orange-min-50);
|
||||
--wa-color-neutral-max-60: var(--wa-color-orange-max-60);
|
||||
--wa-color-neutral-min-60: var(--wa-color-orange-min-60);
|
||||
--wa-color-neutral-max-70: var(--wa-color-orange-max-70);
|
||||
--wa-color-neutral-min-70: var(--wa-color-orange-min-70);
|
||||
|
||||
--wa-color-neutral-on: var(--wa-color-orange-on);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user