mirror of
https://github.com/shoelace-style/webawesome.git
synced 2026-01-19 07:29:14 +00:00
Compare commits
19 Commits
drag-fix
...
lm/theming
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5779e0a8e4 | ||
|
|
67785f64cb | ||
|
|
39ab883802 | ||
|
|
08ef969db3 | ||
|
|
72c4fb7af7 | ||
|
|
827d70b2f5 | ||
|
|
46aa1429a7 | ||
|
|
4b68a741bd | ||
|
|
bd58f7f0a1 | ||
|
|
e8f366835c | ||
|
|
70df6fea1f | ||
|
|
c3ddc303cc | ||
|
|
b57225d5b0 | ||
|
|
c29e4cadf5 | ||
|
|
e5cf6e8757 | ||
|
|
69ea84b490 | ||
|
|
f83707a9e4 | ||
|
|
05bb1f3630 | ||
|
|
b2a2d0b9fd |
@@ -70,6 +70,7 @@
|
||||
"exportparts",
|
||||
"fetchpriority",
|
||||
"fieldsets",
|
||||
"flexbox",
|
||||
"focusin",
|
||||
"focusout",
|
||||
"fontawesome",
|
||||
@@ -151,6 +152,7 @@
|
||||
"ParamagicDev",
|
||||
"peta",
|
||||
"petabit",
|
||||
"pointercancel",
|
||||
"Preact",
|
||||
"preconnect",
|
||||
"prerendered",
|
||||
@@ -204,6 +206,8 @@
|
||||
"thead",
|
||||
"Themer",
|
||||
"tinycolor",
|
||||
"touchcancel",
|
||||
"touchend",
|
||||
"transitionend",
|
||||
"treeitem",
|
||||
"treeshaking",
|
||||
@@ -237,8 +241,6 @@
|
||||
"src/translations/!(en).ts",
|
||||
"**/*.min.js"
|
||||
],
|
||||
"ignoreRegExpList": [
|
||||
"(^|[^a-z])sl[a-z]*(^|[^a-z])"
|
||||
],
|
||||
"ignoreRegExpList": ["(^|[^a-z])sl[a-z]*(^|[^a-z])"],
|
||||
"useGitignore": true
|
||||
}
|
||||
|
||||
195
packages/webawesome/docs/_includes/theming/color-palettes.njk
Normal file
195
packages/webawesome/docs/_includes/theming/color-palettes.njk
Normal file
@@ -0,0 +1,195 @@
|
||||
{% for palette in themer.palettes %}
|
||||
<link rel="stylesheet" href="/dist/styles/color/palettes/{{palette.filename}}" />
|
||||
{% endfor %}
|
||||
|
||||
<div id="color-palettes" class="wa-stack wa-gap-xl">
|
||||
<wa-radio-group id="palette-picker" value="default" class="radio-cards">
|
||||
<span slot="label" class="wa-visually-hidden">Color Palette</span>
|
||||
<div class="wa-grid">
|
||||
{%- for palette in themer.palettes -%}
|
||||
{%- if not palette.isPro -%}
|
||||
<wa-radio value="{{ palette.name | lower }}">
|
||||
<div class="wa-stack wa-gap-xs">
|
||||
<span>{{ palette.name }}</span>
|
||||
<div class="wa-palette-{{ palette.name | lower }} palette-preview wa-grid wa-gap-3xs">
|
||||
<span class="preview-swatch"></span>
|
||||
<span class="preview-swatch"></span>
|
||||
<span class="preview-swatch"></span>
|
||||
<span class="preview-swatch"></span>
|
||||
<span class="preview-swatch"></span>
|
||||
<span class="preview-swatch"></span>
|
||||
<span class="preview-swatch"></span>
|
||||
<span class="preview-swatch"></span>
|
||||
<span class="preview-swatch"></span>
|
||||
<span class="preview-swatch"></span>
|
||||
</div>
|
||||
</div>
|
||||
</wa-radio>
|
||||
{%- else -%}
|
||||
{% raw %}{%- if currentUser.hasPro -%}{% endraw %}
|
||||
<wa-radio value="{{ palette.name | lower }}">
|
||||
<div class="wa-stack wa-gap-xs">
|
||||
<span>{{ palette.name }}</span>
|
||||
<div class="wa-palette-{{ palette.name | lower }} palette-preview wa-grid wa-gap-3xs">
|
||||
<span class="preview-swatch"></span>
|
||||
<span class="preview-swatch"></span>
|
||||
<span class="preview-swatch"></span>
|
||||
<span class="preview-swatch"></span>
|
||||
<span class="preview-swatch"></span>
|
||||
<span class="preview-swatch"></span>
|
||||
<span class="preview-swatch"></span>
|
||||
<span class="preview-swatch"></span>
|
||||
<span class="preview-swatch"></span>
|
||||
<span class="preview-swatch"></span>
|
||||
</div>
|
||||
</div>
|
||||
</wa-radio>
|
||||
{% raw %}{%- endif -%}{% endraw %}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
</div>
|
||||
</wa-radio-group>
|
||||
|
||||
<div id="palette" class="wa-stack wa-gap-xs">
|
||||
{%- for color in themer.colors -%}
|
||||
<div class="color-scale wa-flank wa-gap-xs">
|
||||
<div class="color-name">{{ color }}</div>
|
||||
<div class="color-swatches wa-grid wa-gap-2xs">
|
||||
{%- for tint in themer.tints -%}
|
||||
<wa-copy-button
|
||||
class="color-swatch"
|
||||
copy-label="{{ color }} {{ tint }}"
|
||||
value="var(--wa-color-{{ color }}-{{ tint }})"
|
||||
style="--color: var(--wa-color-{{ color }}-{{ tint }}); --tint: '{{ tint }}'"
|
||||
>
|
||||
<span class="wa-visually-hidden">--wa-color-{{ color }}-{{ tint }}</span>
|
||||
</wa-copy-button>
|
||||
{%- endfor -%}
|
||||
</div>
|
||||
</div>
|
||||
{%- endfor -%}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="module">
|
||||
const paletteContainer = document.getElementById('color-palettes');
|
||||
const palettePreviewPicker = document.getElementById('palette-picker');
|
||||
|
||||
// Set first radio as checked and add initial palette class
|
||||
const firstPaletteRadio = palettePreviewPicker.querySelector('wa-radio');
|
||||
if (firstPaletteRadio) {
|
||||
firstPaletteRadio.checked = true;
|
||||
paletteContainer.classList.add(`wa-palette-${firstPaletteRadio.value}`);
|
||||
}
|
||||
|
||||
// Listen for radio changes
|
||||
palettePreviewPicker.addEventListener('input', function(event) {
|
||||
const selectedValue = event.target.value;
|
||||
|
||||
// Update palette container class
|
||||
const existingThemeClasses = [...paletteContainer.classList].filter(className => className.startsWith('wa-palette-'));
|
||||
existingThemeClasses.forEach(className => paletteContainer.classList.remove(className));
|
||||
paletteContainer.classList.add(`wa-palette-${selectedValue}`);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#color-palettes:has(+ *) {
|
||||
margin-block-end: var(--wa-content-spacing);
|
||||
}
|
||||
|
||||
wa-radio-group.radio-cards {
|
||||
&::part(form-control-input) {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
> .wa-grid {
|
||||
--max-column-count: 3;
|
||||
--min-column-size: 12ch;
|
||||
|
||||
--_max-gap-count: calc(var(--max-column-count) - 1);
|
||||
--_gap-width-sum: calc(var(--_max-gap-count) * var(--wa-space-m));
|
||||
--_max-column-width: calc((100% - var(--_gap-width-sum)) / var(--max-column-count));
|
||||
|
||||
grid-template-columns: repeat(auto-fill, minmax(max(var(--min-column-size), var(--_max-column-width)), 1fr));
|
||||
}
|
||||
|
||||
wa-radio {
|
||||
display: block;
|
||||
inline-size: var(--popover-min-inline-size);
|
||||
background-color: var(--wa-form-control-background-color);
|
||||
border: var(--wa-form-control-border-width) var(--wa-form-control-border-style) var(--wa-color-neutral-border-quiet);
|
||||
border-radius: var(--wa-border-radius-m);
|
||||
padding: 0.75em;
|
||||
font-size: var(--wa-font-size-s);
|
||||
color: var(--wa-color-text-quiet);
|
||||
|
||||
&::part(control) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&::part(label) {
|
||||
font-weight: var(--wa-font-weight-bold);
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
&:hover {
|
||||
border-color: var(--wa-color-neutral-border-normal);
|
||||
}
|
||||
}
|
||||
|
||||
&:state(checked) {
|
||||
border-color: var(--wa-color-neutral-border-loud);
|
||||
box-shadow: 0 0 0 0.0625rem var(--wa-color-neutral-border-loud);
|
||||
color: var(--wa-color-text-normal);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: var(--wa-focus-ring);
|
||||
outline-offset: var(--wa-focus-ring-offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.palette-preview {
|
||||
--min-column-size: 0rem;
|
||||
}
|
||||
|
||||
.preview-swatch {
|
||||
aspect-ratio: 1 / 1;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border-radius: var(--wa-border-radius-s);
|
||||
|
||||
&:nth-child(1) {
|
||||
background-color: var(--wa-color-red);
|
||||
}
|
||||
&:nth-child(2) {
|
||||
background-color: var(--wa-color-orange);
|
||||
}
|
||||
&:nth-child(3) {
|
||||
background-color: var(--wa-color-yellow);
|
||||
}
|
||||
&:nth-child(4) {
|
||||
background-color: var(--wa-color-green);
|
||||
}
|
||||
&:nth-child(5) {
|
||||
background-color: var(--wa-color-cyan);
|
||||
}
|
||||
&:nth-child(6) {
|
||||
background-color: var(--wa-color-blue);
|
||||
}
|
||||
&:nth-child(7) {
|
||||
background-color: var(--wa-color-indigo);
|
||||
}
|
||||
&:nth-child(8) {
|
||||
background-color: var(--wa-color-purple);
|
||||
}
|
||||
&:nth-child(9) {
|
||||
background-color: var(--wa-color-pink);
|
||||
}
|
||||
&:nth-child(10) {
|
||||
background-color: var(--wa-color-gray);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
302
packages/webawesome/docs/_includes/theming/instructions.njk
Normal file
302
packages/webawesome/docs/_includes/theming/instructions.njk
Normal file
@@ -0,0 +1,302 @@
|
||||
{% from "pro-badge.njk" import proBadge %}
|
||||
{% from "free-badge.njk" import freeBadge %}
|
||||
|
||||
<div id="using-themes">
|
||||
<fieldset class="theme-options wa-stack" style="margin-block-end: var(--wa-content-spacing);">
|
||||
<legend style="align-self: start;">Theme Options</legend>
|
||||
<wa-select id="pick-theme" label="Theme" value="default" size="small">
|
||||
<wa-badge slot="end" appearance="accent" pill class="pro pro-only-value">Pro</wa-badge>
|
||||
{%- for theme in themer.themes -%}
|
||||
{%- if not theme.isPro -%}
|
||||
<wa-option
|
||||
value="{{ theme.name | lower }}"
|
||||
data-palette="{{ theme.palette.filename | stripExtension}}"
|
||||
data-brand="{{ theme.colorBrand.color }}"
|
||||
>
|
||||
{{ theme.name | capitalize }}
|
||||
</wa-option>
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{% raw %}{% if currentUser.hasPro %}{% endraw %}
|
||||
{%- for theme in themer.themes -%}
|
||||
{% if loop.first %}<wa-divider></wa-divider>{% endif %}
|
||||
{%- if theme.isPro -%}
|
||||
<wa-option
|
||||
value="{{ theme.name | lower }}"
|
||||
data-palette="{{ theme.palette.filename | stripExtension}}"
|
||||
data-brand="{{ theme.colorBrand.color }}"
|
||||
{% if theme.isPro %}data-is-pro{% endif %}
|
||||
>
|
||||
{{ theme.name | capitalize }}
|
||||
</wa-option>
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{% raw %}{% endif %}{% endraw %}
|
||||
</wa-select>
|
||||
<wa-select id="pick-palette" label="Color Palette" value="default" size="small">
|
||||
<wa-badge slot="end" appearance="accent" pill class="pro pro-only-value">Pro</wa-badge>
|
||||
{%- for palette in themer.palettes -%}
|
||||
{%- if not palette.isPro -%}
|
||||
<wa-option
|
||||
value="{{ palette.name | lower }}"
|
||||
>
|
||||
{{ palette.name | capitalize }}
|
||||
</wa-option>
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{% raw %}{% if currentUser.hasPro %}{% endraw %}
|
||||
{%- for palette in themer.palettes -%}
|
||||
{% if loop.first %}<wa-divider></wa-divider>{% endif %}
|
||||
{%- if palette.isPro -%}
|
||||
<wa-option
|
||||
value="{{ palette.name | lower }}"
|
||||
{% if palette.isPro %}data-is-pro{% endif %}
|
||||
>
|
||||
{{ palette.name | capitalize }}
|
||||
</wa-option>
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{% raw %}{% endif %}{% endraw %}
|
||||
</wa-select>
|
||||
<div class="wa-grid">
|
||||
{%- set colorVariants = ['brand', 'neutral', 'success', 'warning', 'danger'] -%}
|
||||
{%- for colorVariant in colorVariants -%}
|
||||
<wa-select id="pick-{{ colorVariant }}" label="{{ colorVariant | capitalize }}" size="small">
|
||||
<wa-icon slot="start" name="square" class="selected-color"></wa-icon>
|
||||
{%- for color in themer.colors -%}
|
||||
<wa-option value="{{ color }}">
|
||||
{{ color | capitalize }}
|
||||
<wa-icon slot="start" name="square" style="--color: var(--wa-color-{{ color }}); color: var(--color);"></wa-icon>
|
||||
</wa-option>
|
||||
{%- endfor -%}
|
||||
</wa-select>
|
||||
{%- endfor -%}
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<wa-tab-group>
|
||||
<wa-tab panel="cdn"><wa-icon name="rocket-launch" variant="regular"></wa-icon> CDN</wa-tab>
|
||||
<wa-tab panel="npm"><wa-icon name="box-open" variant="regular"></wa-icon> npm</wa-tab>
|
||||
<wa-tab panel="self-hosted"><wa-icon name="arrow-down-to-line" variant="regular"></wa-icon> Self-Hosted</wa-tab>
|
||||
|
||||
<wa-tab-panel name="cdn">
|
||||
<div class="wa-stack wa-gap-xl">
|
||||
<wa-callout variant="neutral" size="small" id="pro-only-callout">
|
||||
<wa-icon name="info-circle" variant="regular"></wa-icon>
|
||||
This combination can only be used on teams with a Pro subscription.
|
||||
</wa-callout>
|
||||
<div id="free-instructions">
|
||||
<strong>For projects on <wa-badge appearance="filled" variant="neutral" pill class="free">Free</wa-badge> teams:</strong>
|
||||
<ol>
|
||||
<li>Head over to your project's <wa-icon name="gear" variant="regular"></wa-icon> <strong>Settings</strong>.</li>
|
||||
<li>For <strong>Theme</strong>, select <wa-icon name="paintbrush" variant="regular"></wa-icon> <strong class="theme-name"></strong>.</li>
|
||||
<li>For <strong>Color Palette</strong>, select <wa-icon name="swatchbook" variant="regular"></wa-icon> <strong class="palette-name"></strong>.</li>
|
||||
<li>Press <strong>Save Changes</strong> to update anywhere you're using your project.</li>
|
||||
<li class="custom-variants">In your own files, apply the following classes to the <code><html></code> element:<br />
|
||||
<pre><code class="variant-classes" class="language-html"></code></pre>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
<div id="pro-instructions">
|
||||
<strong>For projects on <wa-badge appearance="accent" pill class="pro">Pro</wa-badge> teams:</strong>
|
||||
<ol>
|
||||
<li>Head over to your project's <wa-icon name="gear" variant="regular"></wa-icon> <strong>Settings</strong>.</li>
|
||||
<li>Press <wa-icon name="paintbrush" variant="regular"></wa-icon> <strong>Edit Your Theme</strong> to open the Theme Builder.</li>
|
||||
<li>Open <strong>Theme</strong> and select <strong class="theme-name"></strong>.</li>
|
||||
<li>Open <strong>Colors</strong>. On the <strong>Basic</strong> tab, select <strong class="palette-name"></strong>. <span class="custom-variants">On the <strong>Advanced</strong> tab, select your preferred variant colors.</span></li>
|
||||
<li>Press <strong>Save Changes</strong> to update anywhere you're using your project.</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</wa-tab-panel>
|
||||
|
||||
<wa-tab-panel name="npm">
|
||||
<p>To use this theme, include the following stylesheet(s):</p>
|
||||
<pre><code id="stylesheet-imports" class="language-html"></code></pre>
|
||||
<p>Then, apply the following classes to the <code><html></code> element:</p>
|
||||
<pre><code class="html-classes" class="language-html"></code></pre>
|
||||
</wa-tab-panel>
|
||||
|
||||
<wa-tab-panel name="self-hosted">
|
||||
<p>To use this theme, include the following stylesheet(s):</p>
|
||||
<pre><code id="stylesheet-links" class="language-html"></code></pre>
|
||||
<p>Then, apply the following classes to the <code><html></code> element:</p>
|
||||
<pre><code class="html-classes" class="language-html"></code></pre>
|
||||
</wa-tab-panel>
|
||||
</wa-tab-group>
|
||||
|
||||
<script type="module">
|
||||
const themePicker = document.getElementById('pick-theme');
|
||||
const palettePicker = document.getElementById('pick-palette');
|
||||
const brandPicker = document.getElementById('pick-brand');
|
||||
const neutralPicker = document.getElementById('pick-neutral');
|
||||
const successPicker = document.getElementById('pick-success');
|
||||
const warningPicker = document.getElementById('pick-warning');
|
||||
const dangerPicker = document.getElementById('pick-danger');
|
||||
|
||||
const stylesheetLinks = document.getElementById('stylesheet-links');
|
||||
const stylesheetImports = document.getElementById('stylesheet-imports');
|
||||
const htmlClassList = document.querySelectorAll('.html-classes');
|
||||
const variantClassList = document.querySelectorAll('.variant-classes');
|
||||
const themeNames = document.querySelectorAll('.theme-name');
|
||||
const paletteNames = document.querySelectorAll('.palette-name');
|
||||
|
||||
// Default values for color variants
|
||||
const defaultColors = {
|
||||
brand: 'blue',
|
||||
neutral: 'gray',
|
||||
success: 'green',
|
||||
warning: 'yellow',
|
||||
danger: 'red'
|
||||
};
|
||||
|
||||
// Update dynamic instructions based on current picker values
|
||||
function updateInstructions() {
|
||||
const theme = themePicker.value;
|
||||
const palette = palettePicker.value;
|
||||
const brand = brandPicker.value;
|
||||
const neutral = neutralPicker.value;
|
||||
const success = successPicker.value;
|
||||
const warning = warningPicker.value;
|
||||
const danger = dangerPicker.value;
|
||||
|
||||
// Get the default palette for the selected theme
|
||||
const selectedThemeOption = themePicker.querySelector(`wa-option[value="${theme}"]`);
|
||||
const themeDefaultPalette = selectedThemeOption?.getAttribute('data-palette') || 'default';
|
||||
const proOnlyTheme = selectedThemeOption?.hasAttribute('data-is-pro') || false;
|
||||
const proOnlyPalette = palettePicker.querySelector(`wa-option[value="${palette}"]`)?.hasAttribute('data-is-pro') || false;
|
||||
|
||||
// Show/hide Pro-only callout
|
||||
const proOnlyCallout = document.getElementById('pro-only-callout');
|
||||
const freeInstructions = document.getElementById('free-instructions');
|
||||
if (proOnlyTheme || proOnlyPalette) {
|
||||
proOnlyCallout.style.display = '';
|
||||
freeInstructions.style.display = 'none';
|
||||
} else {
|
||||
proOnlyCallout.style.display = 'none';
|
||||
freeInstructions.style.display = '';
|
||||
}
|
||||
|
||||
// Show/hide Pro badge on selected Pro values
|
||||
document.querySelectorAll('.pro-only-value').forEach(badge => {
|
||||
if ((badge.parentElement.id === 'pick-theme' && proOnlyTheme) ||
|
||||
(badge.parentElement.id === 'pick-palette' && proOnlyPalette)) {
|
||||
badge.style.display = '';
|
||||
} else {
|
||||
badge.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
// Build stylesheet links
|
||||
const links = [`<link rel="stylesheet" href="/dist/styles/themes/${theme}.css" />`];
|
||||
const imports = [`import '@awesome.me/webawesome/dist/styles/themes/${theme}.css';`];
|
||||
|
||||
// Only include palette link if it differs from theme's default
|
||||
if (palette !== themeDefaultPalette) {
|
||||
links.push(`<link rel="stylesheet" href="/dist/styles/color/palettes/${palette}.css" />`);
|
||||
imports.push(`import '@awesome.me/webawesome/dist/styles/color/palettes/${palette}.css';`);
|
||||
}
|
||||
|
||||
stylesheetLinks.textContent = links.join('\n');
|
||||
stylesheetImports.textContent = imports.join('\n');
|
||||
|
||||
// Build HTML classes
|
||||
const classes = [`wa-theme-${theme}`];
|
||||
const variantClasses = [];
|
||||
|
||||
// Only include palette class if it differs from theme's default
|
||||
if (palette !== themeDefaultPalette) {
|
||||
classes.push(`wa-palette-${palette}`);
|
||||
}
|
||||
|
||||
// Only include color classes if they differ from defaults
|
||||
if (brand !== defaultColors.brand) {
|
||||
[classes, variantClasses].forEach(classList => classList.push(`wa-brand-${brand}`));
|
||||
}
|
||||
if (neutral !== defaultColors.neutral) {
|
||||
[classes, variantClasses].forEach(classList => classList.push(`wa-neutral-${neutral}`));
|
||||
}
|
||||
if (success !== defaultColors.success) {
|
||||
[classes, variantClasses].forEach(classList => classList.push(`wa-success-${success}`));
|
||||
}
|
||||
if (warning !== defaultColors.warning) {
|
||||
[classes, variantClasses].forEach(classList => classList.push(`wa-warning-${warning}`));
|
||||
}
|
||||
if (danger !== defaultColors.danger) {
|
||||
[classes, variantClasses].forEach(classList => classList.push(`wa-danger-${danger}`));
|
||||
}
|
||||
|
||||
htmlClassList.forEach(instance => {
|
||||
instance.textContent = `class="${classes.join(' ')}"`;
|
||||
});
|
||||
|
||||
variantClassList.forEach(instance => {
|
||||
instance.textContent = `class="${variantClasses.join(' ')}"`;
|
||||
});
|
||||
|
||||
if (variantClasses.length === 0) {
|
||||
document.querySelectorAll('.custom-variants').forEach(el => el.style.display = 'none');
|
||||
} else {
|
||||
document.querySelectorAll('.custom-variants').forEach(el => el.style.display = '');
|
||||
}
|
||||
|
||||
// Update theme and palette names in CDN instructions
|
||||
themeNames.forEach(instance => {
|
||||
instance.textContent = theme;
|
||||
instance.style.textTransform = 'capitalize';
|
||||
});
|
||||
paletteNames.forEach(instance => {
|
||||
instance.textContent = palette;
|
||||
instance.style.textTransform = 'capitalize';
|
||||
});
|
||||
|
||||
// Match color picker icons to selected palette and color
|
||||
[brandPicker, neutralPicker, successPicker, warningPicker, dangerPicker].forEach(picker => {
|
||||
const color = picker.value;
|
||||
const selectedColorIcon = picker.querySelector('wa-icon.selected-color');
|
||||
const optionsColorIcons = picker.querySelectorAll('wa-option wa-icon[slot="start"]');
|
||||
|
||||
if (selectedColorIcon) {
|
||||
selectedColorIcon.style.setProperty('color', `var(--wa-color-${color})`);
|
||||
selectedColorIcon.classList.remove(...[...selectedColorIcon.classList].filter(className => className.startsWith('wa-palette-')));
|
||||
selectedColorIcon.classList.add(`wa-palette-${palette}`);
|
||||
}
|
||||
|
||||
|
||||
optionsColorIcons.forEach(icon => {
|
||||
icon.classList.remove(...[...icon.classList].filter(className => className.startsWith('wa-palette-')));
|
||||
icon.classList.add(`wa-palette-${palette}`);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Set palette and brand based on theme selection
|
||||
themePicker.addEventListener('input', function() {
|
||||
const selectedOption = themePicker.querySelector(`wa-option[value="${themePicker.value}"]`);
|
||||
const palette = selectedOption.getAttribute('data-palette');
|
||||
const brand = selectedOption.getAttribute('data-brand');
|
||||
|
||||
palettePicker.value = palette;
|
||||
brandPicker.value = brand;
|
||||
|
||||
updateInstructions();
|
||||
});
|
||||
|
||||
// Update instructions when any picker changes
|
||||
[palettePicker, brandPicker, neutralPicker, successPicker, warningPicker, dangerPicker].forEach(picker => {
|
||||
picker.addEventListener('input', updateInstructions);
|
||||
});
|
||||
|
||||
// Set initial values
|
||||
themePicker.value = 'default';
|
||||
palettePicker.value = 'default';
|
||||
brandPicker.value = 'blue';
|
||||
neutralPicker.value = 'gray';
|
||||
successPicker.value = 'green';
|
||||
warningPicker.value = 'yellow';
|
||||
dangerPicker.value = 'red';
|
||||
|
||||
// Initial update
|
||||
updateInstructions();
|
||||
</script>
|
||||
27
packages/webawesome/docs/_includes/theming/variants.njk
Normal file
27
packages/webawesome/docs/_includes/theming/variants.njk
Normal file
@@ -0,0 +1,27 @@
|
||||
{% set colorVariants = ["brand", "neutral", "success", "warning", "danger"] %}
|
||||
|
||||
<div id="color-variants" class="wa-stack wa-gap-xs">
|
||||
{%- for colorVariant in colorVariants -%}
|
||||
<div class="color-scale wa-flank wa-gap-xs">
|
||||
<div class="color-name">{{ colorVariant }}</div>
|
||||
<div class="color-swatches wa-grid wa-gap-2xs">
|
||||
{%- for tint in themer.tints -%}
|
||||
<wa-copy-button
|
||||
class="color-swatch"
|
||||
copy-label="{{ colorVariant }} {{ tint }}"
|
||||
value="var(--wa-color-{{ colorVariant }}-{{ tint }})"
|
||||
style="--color: var(--wa-color-{{ colorVariant }}-{{ tint }}); --tint: '{{ tint }}'"
|
||||
>
|
||||
<span class="wa-visually-hidden">--wa-color-{{ colorVariant }}-{{ tint }}</span>
|
||||
</wa-copy-button>
|
||||
{%- endfor -%}
|
||||
</div>
|
||||
</div>
|
||||
{%- endfor -%}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#color-variants:has(+ *) {
|
||||
margin-block-end: var(--wa-content-spacing);
|
||||
}
|
||||
</style>
|
||||
@@ -10,6 +10,13 @@
|
||||
:root {
|
||||
--wa-brand-orange: #f36944;
|
||||
--wa-brand-grey: #30323b;
|
||||
|
||||
/* layout-based example style aspects */
|
||||
--layout-example-border: var(--wa-border-width-s) dashed var(--wa-color-neutral-border-normal);
|
||||
--layout-example-border-radius: var(--wa-border-radius-l);
|
||||
--layout-example-padding: var(--wa-space-s);
|
||||
--layout-example-element-background: var(--wa-color-indigo-60);
|
||||
--layout-example-element-border-radius: var(--wa-border-radius-m);
|
||||
}
|
||||
|
||||
.wa-dark .only-light,
|
||||
@@ -627,6 +634,68 @@ table.colors {
|
||||
--icon-color: var(--wa-color-success-fill-quiet);
|
||||
}
|
||||
|
||||
/* Theming */
|
||||
|
||||
.color-scale.wa-flank {
|
||||
--flank-size: 6ch;
|
||||
--content-percentage: 88%;
|
||||
--reserved-tint-space: calc(var(--wa-font-size-xs) + var(--wa-space-xs)); /* TODO: rename */
|
||||
margin-bottom: var(--reserved-tint-space);
|
||||
|
||||
.color-swatches.wa-grid {
|
||||
--min-column-size: 2.5rem;
|
||||
grid-row-gap: calc(var(--reserved-tint-space) + var(--wa-space-xs));
|
||||
}
|
||||
|
||||
.color-name {
|
||||
font-weight: var(--wa-font-weight-bold);
|
||||
text-transform: capitalize;
|
||||
font-size: var(--wa-font-size-s);
|
||||
color: var(--wa-color-text-normal);
|
||||
}
|
||||
|
||||
wa-copy-button.color-swatch {
|
||||
padding: 0;
|
||||
aspect-ratio: 1;
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
content: var(--tint);
|
||||
position: absolute;
|
||||
bottom: calc(-1 * var(--reserved-tint-space));
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: var(--wa-font-size-xs);
|
||||
line-height: 1;
|
||||
color: var(--wa-color-text-quiet);
|
||||
text-align: center;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
&::part(button) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
background-color: var(--color);
|
||||
border-radius: var(--wa-border-radius-s);
|
||||
transition: transform 0.1s ease, box-shadow 0.1s ease;
|
||||
}
|
||||
|
||||
&:hover::part(button) {
|
||||
transform: scale(1.075);
|
||||
box-shadow: var(--wa-shadow-s);
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&::part(copy-icon),
|
||||
&::part(success-icon),
|
||||
&::part(error-icon) {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Layout Examples */
|
||||
.layout-example-boundary {
|
||||
border: var(--wa-border-width-s) dashed var(--wa-color-neutral-border-normal);
|
||||
|
||||
@@ -41,7 +41,7 @@ Use the `--spacing` custom property to change the amount of space between the di
|
||||
|
||||
### Orientation
|
||||
|
||||
The default orientation for dividers is `horizontal`. Set `orientation` attribute to `vertical` to draw a vertical divider. The divider will span the full height of its container.
|
||||
The default orientation for dividers is `horizontal`. Set `orientation` attribute to `vertical` to draw a vertical divider. The divider will span the full height of its [Flexbox](https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/CSS_layout/Flexbox) or [CSS Grid](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/grid) container.
|
||||
|
||||
```html {.example}
|
||||
<div style="display: flex; align-items: center;">
|
||||
@@ -53,6 +53,10 @@ The default orientation for dividers is `horizontal`. Set `orientation` attribut
|
||||
</div>
|
||||
```
|
||||
|
||||
:::info
|
||||
If your container isn't Flexbox or CSS Grid, you may need to set an explicit height for the divider.
|
||||
:::
|
||||
|
||||
### Dropdown Dividers
|
||||
|
||||
Use dividers in [dropdowns](/docs/components/dropdown) to visually group dropdown items.
|
||||
|
||||
@@ -13,9 +13,18 @@ Components with the <wa-badge variant="warning">Experimental</wa-badge> badge sh
|
||||
|
||||
## Next
|
||||
|
||||
- Fixed a bug in `<wa-combobox>` that prevented the listbox from opening when options were preselected [issue:1883]
|
||||
- Added `justify-content` CSS utilities [pr:1930]
|
||||
- Added missing `.wa-gap-4xl` utility class [pr:1931]
|
||||
- Added `pointercancel` and `touchcancel` event handling to draggable elements to prevent drags from getting stuck
|
||||
- Added `wa-justify-content-*` utility classes [pr:1930]
|
||||
- Added missing `wa-gap-4xl` utility class [pr:1931]
|
||||
- Fixed a bug in `<wa-combobox>` that prevented the listbox from opening when options were preselected [issue:1883]
|
||||
- Fixed a bug in `<wa-popup>` and `<wa-dropdown-item>` that caused an error when removing a popup while it was opening [issue:1910]
|
||||
- Fixed a bug in `<wa-popup>` and `<wa-dropdown>` that caused errors when shadow DOM queries returned null [issue:1911]
|
||||
- Fixed a bug in `<wa-combobox>` that prevented the listbox from opening when options were preselected [issue:1883]
|
||||
- Fixed a bug in draggable elements that caused a TypeError on `touchend` events when `event.touches` was empty
|
||||
- Fixed a bug in `<wa-tree-item>` that caused the cursor to show a pointer when no expand icon was present [pr:1936]
|
||||
- Modified `wa-align-items-*` utility classes to apply `display: flex` by default [pr:1943]
|
||||
|
||||
## 3.1.0
|
||||
|
||||
|
||||
165
packages/webawesome/docs/docs/theming/index.md
Normal file
165
packages/webawesome/docs/docs/theming/index.md
Normal file
@@ -0,0 +1,165 @@
|
||||
---
|
||||
title: Theming
|
||||
description: TODO
|
||||
layout: page-outline
|
||||
---
|
||||
|
||||
Web Awesome themes apply a cohesive look and feel across the entire library. A theme is a collection of predefined CSS custom properties that cover a range of styles from colors to transitions. We call these CSS custom properties design tokens.
|
||||
|
||||
There are 11 handcrafted themes to choose from; 3 are free to use with an additional 9 available in Web Awesome Pro. You can also build your own manually with CSS or with our Pro Theme Builder.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Themes are made up of several layers of increasing specificity, each represented by a CSS class on the document.
|
||||
|
||||
### Color Palette
|
||||
`.wa-palette-{name}`
|
||||
|
||||
Color palettes give you a full spectrum of colors to use in your project. A color palette defines 10 hues — red, orange, yellow, green, cyan, blue, indigo, purple, pink, and gray — each with 11 tints. Tints are assigned numbers that correlate to their lightness.
|
||||
|
||||
There are 9 specially crafted color palettes; 3 are free to use with an additional 6 available in Web Awesome Pro.
|
||||
|
||||
{% include 'theming/color-palettes.njk' %}
|
||||
|
||||
Your color palette is determined by `class="wa-palette-{name}"` on the `<html>` element. If no class is specified, the default color palette for your chosen theme is used.
|
||||
|
||||
### Variants
|
||||
`.wa-{variant}-{hue}`
|
||||
|
||||
Variants convey a specific meaning through color. There are five variants:
|
||||
- **Brand** for product recognition
|
||||
- **Neutral** for generic and ordinary content
|
||||
- **Success** for validity or confirmation
|
||||
- **Warning** for caution or uncertainty
|
||||
- **Danger** for errors or risk
|
||||
|
||||
Brand and neutral are used by nearly every element, component, and pattern across the library. Success, warning, and danger are used selectively by components that could benefit from semantic reinforcement, such as buttons and callouts.
|
||||
|
||||
{% include 'theming/variants.njk' %}
|
||||
|
||||
Any hue from a color palette can be assigned to a variant. Each variant is determined by `class="wa-{variant}-{hue}"` on the `<html>` element. If no class is specified:
|
||||
- **Brand** defaults to <wa-icon name="square" style="color: var(--wa-color-blue);"></wa-icon> **blue**
|
||||
- **Neutral** defaults to <wa-icon name="square" style="color: var(--wa-color-gray);"></wa-icon> **gray**
|
||||
- **Success** defaults to <wa-icon name="square" style="color: var(--wa-color-green);"></wa-icon> **green**
|
||||
- **Warning** defaults to <wa-icon name="square" style="color: var(--wa-color-yellow);"></wa-icon> **yellow**
|
||||
- **Danger** defaults to <wa-icon name="square" style="color: var(--wa-color-red);"></wa-icon> **red**
|
||||
|
||||
### Theme Styles
|
||||
`.wa-theme-{name}`
|
||||
|
||||
Theme styles assign specific tints from your chosen variant colors — along with qualities like fonts, borders, space, and shadows — to design tokens that style elements and components. Themes may also contain custom CSS overrides to change the default look of components.
|
||||
|
||||
TODO: Add theme preview
|
||||
|
||||
Your theme is determined by `class="wa-theme-{name}"` on the `<html>` element. If no class is specified, the default theme is used.
|
||||
|
||||
### Light and Dark Mode
|
||||
`.wa-light` | `.wa-dark`
|
||||
|
||||
Every theme is designed to adapt to light and dark mode. Light mode styles are applied by default, but you can apply a specific color scheme to an entire page or just a section with `class="wa-light"` or `class="wa-dark"`.
|
||||
|
||||
```html {.example}
|
||||
<div class="wa-grid">
|
||||
|
||||
<wa-card class="wa-light">
|
||||
<div slot="header" class="wa-split">
|
||||
<h4 class="wa-heading-m">Light</h4>
|
||||
<wa-icon name="brightness"></wa-icon>
|
||||
</div>
|
||||
<div class="wa-stack">
|
||||
<wa-input label="# of Waffles" type="number" value="3"></wa-input>
|
||||
<wa-select label="Toppings" multiple value="jelly-beans">
|
||||
<wa-option value="whipped-cream">Whipped cream</wa-option>
|
||||
<wa-option value="hershey">Hershey's Kisses</wa-option>
|
||||
<wa-option value="jelly-beans">Jelly beans</wa-option>
|
||||
</wa-select>
|
||||
<wa-button appearance="filled" variant="brand">
|
||||
<wa-icon slot="start" name="waffle"></wa-icon>
|
||||
Make Waffles
|
||||
</wa-button>
|
||||
</div>
|
||||
</wa-card>
|
||||
|
||||
<wa-card class="wa-dark">
|
||||
<div slot="header" class="wa-split">
|
||||
<h4 class="wa-heading-m">Dark</h4>
|
||||
<wa-icon name="moon-stars"></wa-icon>
|
||||
</div>
|
||||
<div class="wa-stack">
|
||||
<wa-input label="# of Waffles" type="number" value="3"></wa-input>
|
||||
<wa-select label="Toppings" multiple value="jelly-beans">
|
||||
<wa-option value="whipped-cream">Whipped cream</wa-option>
|
||||
<wa-option value="hershey">Hershey's Kisses</wa-option>
|
||||
<wa-option value="jelly-beans">Jelly beans</wa-option>
|
||||
</wa-select>
|
||||
<wa-button appearance="filled" variant="brand">
|
||||
<wa-icon slot="start" name="waffle"></wa-icon>
|
||||
Make Waffles
|
||||
</wa-button>
|
||||
</div>
|
||||
</wa-card>
|
||||
|
||||
</div>
|
||||
```
|
||||
|
||||
#### Inverting the Color Scheme
|
||||
`.wa-invert`
|
||||
|
||||
You can force a section to behave like `.wa-dark` in light mode and like `.wa-light` in dark mode by using `class="wa-invert"`.
|
||||
|
||||
```html {.example}
|
||||
<p>This card will always use the opposite of the color scheme applied to the docs.</p>
|
||||
|
||||
<wa-card class="wa-invert">
|
||||
<div slot="header" class="wa-split">
|
||||
<h4 class="wa-heading-m">Invert</h4>
|
||||
<wa-icon name="swap"></wa-icon>
|
||||
</div>
|
||||
<div class="wa-flank:end wa-align-items-end">
|
||||
<wa-select label="Location" value="upside-down">
|
||||
<wa-option value="lab">Hawkins Lab</wa-option>
|
||||
<wa-option value="mall">Starcourt Mall</wa-option>
|
||||
<wa-option value="upside-down">The Upside Down</wa-option>
|
||||
</wa-select>
|
||||
<wa-button id="go-button" appearance="filled" variant="brand">
|
||||
<wa-icon label="Go" name="person-to-portal" family="duotone"></wa-icon>
|
||||
</wa-button>
|
||||
<wa-tooltip for="go-button">
|
||||
Go!
|
||||
</wa-tooltip>
|
||||
</div>
|
||||
</wa-card>
|
||||
```
|
||||
|
||||
#### Detecting Color Scheme Preference
|
||||
|
||||
While both light and dark mode styles are built-in to all themes, Web Awesome doesn't automatically detect the user's color scheme preference. We recommend doing this at the application level.
|
||||
|
||||
Follow these best practices for supporting both light and dark mode:
|
||||
- Check for [`prefers-color-scheme`](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@media/prefers-color-scheme) and use its value by default
|
||||
- Allow the user to override this setting in your app
|
||||
- Remember the user's preference and restore it on subsequent visits
|
||||
|
||||
Let's assume you store the user's color scheme preference for your app in a variable called `colorScheme` (values: `auto` | `light` | `dark`). You can use the following JS snippet to apply `class="wa-dark"` to the `<html>` element accordingly:
|
||||
|
||||
```js
|
||||
const systemDark = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
const applyDark = function (event = systemDark) {
|
||||
const isDark = colorScheme === 'auto' ? event.matches : colorScheme === 'dark';
|
||||
document.documentElement.classList.toggle('wa-dark', isDark);
|
||||
};
|
||||
systemDark.addEventListener('change', applyDark);
|
||||
applyDark();
|
||||
```
|
||||
|
||||
## Using Themes
|
||||
|
||||
Use and update themes instantly via CDN with [Web Awesome projects](/teams). Or, assemble the pieces together using npm or in your self-hosted app.
|
||||
|
||||
Select your favorite options and follow the instructions for your preferred method.
|
||||
|
||||
{% include 'theming/instructions.njk' %}
|
||||
|
||||
## Creating Your Own
|
||||
|
||||
TODO
|
||||
@@ -7,15 +7,16 @@ tags: layoutUtilities
|
||||
|
||||
<style>
|
||||
.preview-wrapper {
|
||||
border: var(--wa-border-width-s) dashed var(--wa-color-neutral-border-normal);
|
||||
border: var(--layout-example-border);
|
||||
border-radius: var(--wa-border-radius-m);
|
||||
min-block-size: 3em;
|
||||
min-inline-size: 5em;
|
||||
padding: var(--wa-space-2xs);
|
||||
}
|
||||
|
||||
.preview-block {
|
||||
aspect-ratio: 1 / 1;
|
||||
background-color: var(--wa-color-neutral-fill-loud);
|
||||
background-color: var(--layout-example-element-background);
|
||||
border-radius: var(--wa-border-radius-s);
|
||||
min-block-size: 1em;
|
||||
}
|
||||
|
||||
@@ -7,19 +7,21 @@ tags: layoutUtilities
|
||||
|
||||
<style>
|
||||
:is(.wa-flank, .wa-grid, .wa-stack) > [class*='wa-cluster']:has(div:empty) {
|
||||
border: var(--wa-border-width-s) dashed var(--wa-color-neutral-border-normal);
|
||||
border-radius: var(--wa-border-radius-l);
|
||||
padding: var(--wa-space-s);
|
||||
border: var(--layout-example-border);
|
||||
border-radius: var(--layout-example-border-radius);
|
||||
padding: var(--layout-example-padding);
|
||||
}
|
||||
|
||||
[class*='wa-cluster'] div:empty {
|
||||
background-color: var(--wa-color-indigo-60);
|
||||
border-radius: var(--wa-border-radius-m);
|
||||
background-color: var(--layout-example-element-background);
|
||||
border-radius: var(--layout-example-element-border-radius);
|
||||
min-block-size: 4rem;
|
||||
min-inline-size: 4rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
{{ description }}
|
||||
|
||||
```html {.example}
|
||||
<div class="wa-cluster">
|
||||
<div></div>
|
||||
|
||||
@@ -7,20 +7,20 @@ tags: layoutUtilities
|
||||
|
||||
<style>
|
||||
:is(.wa-flank, .wa-grid, .wa-stack) > [class*='wa-flank']:has(div:empty) {
|
||||
border: var(--wa-border-width-s) dashed var(--wa-color-neutral-border-normal);
|
||||
border-radius: var(--wa-border-radius-l);
|
||||
padding: var(--wa-space-s);
|
||||
border: var(--layout-example-border);
|
||||
border-radius: var(--layout-example-border-radius);
|
||||
padding: var(--layout-example-padding);
|
||||
}
|
||||
|
||||
[class*='wa-flank'] div:empty {
|
||||
background-color: var(--wa-color-indigo-60);
|
||||
border-radius: var(--wa-border-radius-m);
|
||||
background-color: var(--layout-example-element-background);
|
||||
border-radius: var(--layout-example-element-border-radius);
|
||||
min-block-size: 4rem;
|
||||
min-inline-size: 4rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
When space is limited, the items wrap.
|
||||
{{ description }} When space is limited, the items wrap.
|
||||
|
||||
```html {.example}
|
||||
<div class="wa-flank">
|
||||
|
||||
@@ -7,18 +7,20 @@ tags: layoutUtilities
|
||||
|
||||
<style>
|
||||
[class*='wa-frame']:has(div:empty) {
|
||||
border: var(--wa-border-width-s) dashed var(--wa-color-neutral-border-normal);
|
||||
padding: var(--wa-space-s);
|
||||
border: var(--layout-example-border);
|
||||
padding: var(--layout-example-padding);
|
||||
}
|
||||
|
||||
[class*='wa-frame'] div:empty {
|
||||
background-color: var(--wa-color-indigo-60);
|
||||
border-radius: var(--wa-border-radius-m);
|
||||
background-color: var(--layout-example-element-background);
|
||||
border-radius: var(--layout-example-element-border-radius);
|
||||
min-block-size: 4rem;
|
||||
min-inline-size: 4rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
{{ description }}
|
||||
|
||||
```html {.example}
|
||||
<div class="wa-frame" style="max-inline-size: 20rem;">
|
||||
<div></div>
|
||||
|
||||
@@ -6,11 +6,19 @@ tags: layoutUtilities
|
||||
---
|
||||
|
||||
<style>
|
||||
.preview-wrapper {
|
||||
border: var(--layout-example-border);
|
||||
border-radius: var(--wa-border-radius-m);
|
||||
min-block-size: 3em;
|
||||
min-inline-size: 5em;
|
||||
padding: var(--wa-space-2xs);
|
||||
}
|
||||
|
||||
.preview-block {
|
||||
aspect-ratio: 1 / 1;
|
||||
background-color: var(--wa-color-neutral-fill-loud);
|
||||
background-color: var(--layout-example-element-background);
|
||||
border-radius: var(--wa-border-radius-s);
|
||||
min-block-size: 1.5em;
|
||||
min-block-size: 1em;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -20,15 +28,15 @@ Besides `wa-gap-0`, which sets `gap` to zero, each class corresponds to one of t
|
||||
|
||||
| Class Name | `gap` Value | Preview |
|
||||
| ------------ | ---------------- | ----------------------------------------------------------------------------------------------------------- |
|
||||
| `wa-gap-0` | `0` | <div class="wa-cluster wa-gap-0"><div class="preview-block"></div><div class="preview-block"></div></div> |
|
||||
| `wa-gap-3xs` | `--wa-space-3xs` | <div class="wa-cluster wa-gap-3xs"><div class="preview-block"></div><div class="preview-block"></div></div> |
|
||||
| `wa-gap-2xs` | `--wa-space-2xs` | <div class="wa-cluster wa-gap-2xs"><div class="preview-block"></div><div class="preview-block"></div></div> |
|
||||
| `wa-gap-xs` | `--wa-space-xs` | <div class="wa-cluster wa-gap-xs"><div class="preview-block"></div><div class="preview-block"></div></div> |
|
||||
| `wa-gap-s` | `--wa-space-s` | <div class="wa-cluster wa-gap-s"><div class="preview-block"></div><div class="preview-block"></div></div> |
|
||||
| `wa-gap-m` | `--wa-space-m` | <div class="wa-cluster wa-gap-m"><div class="preview-block"></div><div class="preview-block"></div></div> |
|
||||
| `wa-gap-l` | `--wa-space-l` | <div class="wa-cluster wa-gap-l"><div class="preview-block"></div><div class="preview-block"></div></div> |
|
||||
| `wa-gap-xl` | `--wa-space-xl` | <div class="wa-cluster wa-gap-xl"><div class="preview-block"></div><div class="preview-block"></div></div> |
|
||||
| `wa-gap-2xl` | `--wa-space-2xl` | <div class="wa-cluster wa-gap-2xl"><div class="preview-block"></div><div class="preview-block"></div></div> |
|
||||
| `wa-gap-3xl` | `--wa-space-3xl` | <div class="wa-cluster wa-gap-3xl"><div class="preview-block"></div><div class="preview-block"></div></div> |
|
||||
| `wa-gap-0` | `0` | <div class="preview-wrapper wa-cluster wa-gap-0"><div class="preview-block"></div><div class="preview-block"></div></div> |
|
||||
| `wa-gap-3xs` | `--wa-space-3xs` | <div class="preview-wrapper wa-cluster wa-gap-3xs"><div class="preview-block"></div><div class="preview-block"></div></div> |
|
||||
| `wa-gap-2xs` | `--wa-space-2xs` | <div class="preview-wrapper wa-cluster wa-gap-2xs"><div class="preview-block"></div><div class="preview-block"></div></div> |
|
||||
| `wa-gap-xs` | `--wa-space-xs` | <div class="preview-wrapper wa-cluster wa-gap-xs"><div class="preview-block"></div><div class="preview-block"></div></div> |
|
||||
| `wa-gap-s` | `--wa-space-s` | <div class="preview-wrapper wa-cluster wa-gap-s"><div class="preview-block"></div><div class="preview-block"></div></div> |
|
||||
| `wa-gap-m` | `--wa-space-m` | <div class="preview-wrapper wa-cluster wa-gap-m"><div class="preview-block"></div><div class="preview-block"></div></div> |
|
||||
| `wa-gap-l` | `--wa-space-l` | <div class="preview-wrapper wa-cluster wa-gap-l"><div class="preview-block"></div><div class="preview-block"></div></div> |
|
||||
| `wa-gap-xl` | `--wa-space-xl` | <div class="preview-wrapper wa-cluster wa-gap-xl"><div class="preview-block"></div><div class="preview-block"></div></div> |
|
||||
| `wa-gap-2xl` | `--wa-space-2xl` | <div class="preview-wrapper wa-cluster wa-gap-2xl"><div class="preview-block"></div><div class="preview-block"></div></div> |
|
||||
| `wa-gap-3xl` | `--wa-space-3xl` | <div class="preview-wrapper wa-cluster wa-gap-3xl"><div class="preview-block"></div><div class="preview-block"></div></div> |
|
||||
<!-- Pending 3.2.0 release -->
|
||||
<!-- | `wa-gap-4xl` | `--wa-space-4xl` | <div class="wa-cluster wa-gap-4xl"><div class="preview-block"></div><div class="preview-block"></div></div> | -->
|
||||
<!-- | `wa-gap-4xl` | `--wa-space-4xl` | <div class="preview-wrapper wa-cluster wa-gap-4xl"><div class="preview-block"></div><div class="preview-block"></div></div> | -->
|
||||
|
||||
@@ -7,19 +7,21 @@ tags: layoutUtilities
|
||||
|
||||
<style>
|
||||
:is(.wa-flank, .wa-grid, .wa-stack) > [class*='wa-grid']:has(div:empty) {
|
||||
border: var(--wa-border-width-s) dashed var(--wa-color-neutral-border-normal);
|
||||
border-radius: var(--wa-border-radius-l);
|
||||
padding: var(--wa-space-s);
|
||||
border: var(--layout-example-border);
|
||||
border-radius: var(--layout-example-border-radius);
|
||||
padding: var(--layout-example-padding);
|
||||
}
|
||||
|
||||
[class*='wa-grid'] div:empty {
|
||||
background-color: var(--wa-color-indigo-60);
|
||||
border-radius: var(--wa-border-radius-m);
|
||||
background-color: var(--layout-example-element-background);
|
||||
border-radius: var(--layout-example-element-border-radius);
|
||||
min-block-size: 4rem;
|
||||
min-inline-size: 4rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
{{ description }}
|
||||
|
||||
```html {.example}
|
||||
<div class="wa-grid">
|
||||
<div></div>
|
||||
|
||||
@@ -9,15 +9,16 @@ unlisted: true
|
||||
|
||||
<style>
|
||||
.preview-wrapper {
|
||||
border: var(--wa-border-width-s) dashed var(--wa-color-neutral-border-normal);
|
||||
border: var(--layout-example-border);
|
||||
border-radius: var(--wa-border-radius-m);
|
||||
min-block-size: 3em;
|
||||
min-inline-size: 5em;
|
||||
padding: var(--wa-space-2xs);
|
||||
}
|
||||
|
||||
.preview-block {
|
||||
aspect-ratio: 1 / 1;
|
||||
background-color: var(--wa-color-neutral-fill-loud);
|
||||
background-color: var(--layout-example-element-background);
|
||||
border-radius: var(--wa-border-radius-s);
|
||||
min-block-size: 1em;
|
||||
}
|
||||
|
||||
@@ -7,19 +7,21 @@ tags: layoutUtilities
|
||||
|
||||
<style>
|
||||
:is(.wa-flank, .wa-grid, .wa-stack) > [class*='wa-split']:has(div:empty) {
|
||||
border: var(--wa-border-width-s) dashed var(--wa-color-neutral-border-normal);
|
||||
border-radius: var(--wa-border-radius-l);
|
||||
padding: var(--wa-space-s);
|
||||
border: var(--layout-example-border);
|
||||
border-radius: var(--layout-example-border-radius);
|
||||
padding: var(--layout-example-padding);
|
||||
}
|
||||
|
||||
[class*='wa-split'] div:empty {
|
||||
background-color: var(--wa-color-indigo-60);
|
||||
border-radius: var(--wa-border-radius-m);
|
||||
background-color: var(--layout-example-element-background);
|
||||
border-radius: var(--layout-example-element-border-radius);
|
||||
min-block-size: 4rem;
|
||||
min-inline-size: 4rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
{{ description }}
|
||||
|
||||
```html {.example}
|
||||
<div class="wa-split">
|
||||
<div></div>
|
||||
|
||||
@@ -7,19 +7,21 @@ tags: layoutUtilities
|
||||
|
||||
<style>
|
||||
:is(.wa-flank, .wa-grid, .wa-stack) > [class*='wa-stack']:has(div:empty) {
|
||||
border: var(--wa-border-width-s) dashed var(--wa-color-neutral-border-normal);
|
||||
border-radius: var(--wa-border-radius-l);
|
||||
padding: var(--wa-space-s);
|
||||
border: var(--layout-example-border);
|
||||
border-radius: var(--layout-example-border-radius);
|
||||
padding: var(--layout-example-padding);
|
||||
}
|
||||
|
||||
[class*='wa-stack'] div:empty {
|
||||
background-color: var(--wa-color-indigo-60);
|
||||
border-radius: var(--wa-border-radius-m);
|
||||
background-color: var(--layout-example-element-background);
|
||||
border-radius: var(--layout-example-element-border-radius);
|
||||
min-block-size: 4rem;
|
||||
min-inline-size: 4rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
{{ description }}
|
||||
|
||||
```html {.example}
|
||||
<div class="wa-stack">
|
||||
<div></div>
|
||||
|
||||
@@ -37,7 +37,7 @@ export default function (plop) {
|
||||
},
|
||||
{
|
||||
type: 'add',
|
||||
path: '../../src/components/{{ tagWithoutPrefix tag }}/{{ tagWithoutPrefix tag }}.css',
|
||||
path: '../../src/components/{{ tagWithoutPrefix tag }}/{{ tagWithoutPrefix tag }}.styles.ts',
|
||||
templateFile: 'templates/component/styles.hbs',
|
||||
},
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@ import { html } from 'lit';
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
import styles from './{{ tagWithoutPrefix tag }}.css';
|
||||
import styles from './{{ tagWithoutPrefix tag }}.styles.js';
|
||||
|
||||
/**
|
||||
* @summary Short summary of the component's intended use.
|
||||
@@ -21,7 +21,7 @@ import styles from './{{ tagWithoutPrefix tag }}.css';
|
||||
*/
|
||||
@customElement("{{ tag }}")
|
||||
export default class {{ properCase tag }} extends WebAwesomeElement {
|
||||
static css = styles;
|
||||
static css = [styles];
|
||||
|
||||
/** An example attribute. */
|
||||
@property() attr = 'example';
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
:host {
|
||||
display: block;
|
||||
import { css } from "lit";
|
||||
|
||||
export default css`
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
`
|
||||
|
||||
@@ -155,20 +155,21 @@ export default class WaDropdownItem extends WebAwesomeElement {
|
||||
|
||||
/** Opens the submenu. */
|
||||
async openSubmenu() {
|
||||
if (!this.hasSubmenu || !this.submenuElement) return;
|
||||
const submenu = this.submenuElement;
|
||||
if (!this.hasSubmenu || !submenu || !this.isConnected) return;
|
||||
|
||||
// Notify parent dropdown to handle positioning
|
||||
this.notifyParentOfOpening();
|
||||
|
||||
// Use Popover API to show the submenu
|
||||
this.submenuElement.showPopover();
|
||||
this.submenuElement.hidden = false;
|
||||
this.submenuElement.setAttribute('data-visible', '');
|
||||
submenu.showPopover?.();
|
||||
submenu.hidden = false;
|
||||
submenu.setAttribute('data-visible', '');
|
||||
this.submenuOpen = true;
|
||||
this.setAttribute('aria-expanded', 'true');
|
||||
|
||||
// Animate the submenu
|
||||
await animateWithClass(this.submenuElement, 'show');
|
||||
await animateWithClass(submenu, 'show');
|
||||
|
||||
// Set focus to the first submenu item
|
||||
setTimeout(() => {
|
||||
@@ -210,16 +211,19 @@ export default class WaDropdownItem extends WebAwesomeElement {
|
||||
|
||||
/** Closes the submenu. */
|
||||
async closeSubmenu() {
|
||||
if (!this.hasSubmenu || !this.submenuElement) return;
|
||||
const submenu = this.submenuElement;
|
||||
if (!this.hasSubmenu || !submenu) return;
|
||||
|
||||
this.submenuOpen = false;
|
||||
this.setAttribute('aria-expanded', 'false');
|
||||
|
||||
if (!this.submenuElement.hidden) {
|
||||
await animateWithClass(this.submenuElement, 'hide');
|
||||
this.submenuElement.hidden = true;
|
||||
this.submenuElement.removeAttribute('data-visible');
|
||||
this.submenuElement.hidePopover();
|
||||
if (!submenu.hidden) {
|
||||
await animateWithClass(submenu, 'hide');
|
||||
if (submenu?.isConnected) {
|
||||
submenu.hidden = true;
|
||||
submenu.removeAttribute('data-visible');
|
||||
submenu.hidePopover?.();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -138,9 +138,9 @@ export default class WaDropdown extends WebAwesomeElement {
|
||||
|
||||
/** Gets all dropdown items slotted in the menu. */
|
||||
private getItems(includeDisabled = false): WaDropdownItem[] {
|
||||
const items = this.defaultSlot
|
||||
.assignedElements({ flatten: true })
|
||||
.filter(el => el.localName === 'wa-dropdown-item') as WaDropdownItem[];
|
||||
const items = (this.defaultSlot?.assignedElements({ flatten: true }) ?? []).filter(
|
||||
el => el.localName === 'wa-dropdown-item',
|
||||
) as WaDropdownItem[];
|
||||
|
||||
return includeDisabled ? items : items.filter(item => !item.disabled);
|
||||
}
|
||||
@@ -165,9 +165,9 @@ export default class WaDropdown extends WebAwesomeElement {
|
||||
|
||||
/** Syncs item sizes with the dropdown's size property. */
|
||||
private syncItemSizes() {
|
||||
const items = this.defaultSlot
|
||||
.assignedElements({ flatten: true })
|
||||
.filter(el => el.localName === 'wa-dropdown-item') as WaDropdownItem[];
|
||||
const items = (this.defaultSlot?.assignedElements({ flatten: true }) ?? []).filter(
|
||||
el => el.localName === 'wa-dropdown-item',
|
||||
) as WaDropdownItem[];
|
||||
items.forEach(item => (item.size = this.size));
|
||||
}
|
||||
|
||||
@@ -230,7 +230,7 @@ export default class WaDropdown extends WebAwesomeElement {
|
||||
/** Shows the dropdown menu. This should only be called from within updated(). */
|
||||
private async showMenu() {
|
||||
const anchor = this.getTrigger();
|
||||
if (!anchor) return;
|
||||
if (!anchor || !this.popup || !this.menu) return;
|
||||
|
||||
const showEvent = new WaShowEvent();
|
||||
this.dispatchEvent(showEvent);
|
||||
@@ -270,6 +270,8 @@ export default class WaDropdown extends WebAwesomeElement {
|
||||
|
||||
/** Hides the dropdown menu. This should only be called from within updated(). */
|
||||
private async hideMenu() {
|
||||
if (!this.popup || !this.menu) return;
|
||||
|
||||
const hideEvent = new WaHideEvent({ source: this });
|
||||
this.dispatchEvent(hideEvent);
|
||||
if (hideEvent.defaultPrevented) {
|
||||
@@ -720,12 +722,12 @@ export default class WaDropdown extends WebAwesomeElement {
|
||||
nativeButton.setAttribute('aria-haspopup', 'menu');
|
||||
nativeButton.setAttribute('aria-expanded', this.open ? 'true' : 'false');
|
||||
|
||||
this.menu.setAttribute('aria-expanded', 'false');
|
||||
this.menu?.setAttribute('aria-expanded', 'false');
|
||||
}
|
||||
|
||||
render() {
|
||||
// On initial render, we want to use this.open, for everything else, we sync off of this.popup.active to get animations working.
|
||||
let active = this.hasUpdated ? this.popup.active : this.open;
|
||||
let active = this.hasUpdated ? this.popup?.active : this.open;
|
||||
|
||||
return html`
|
||||
<wa-popup
|
||||
|
||||
@@ -286,11 +286,11 @@ export default class WaPopup extends WebAwesomeElement {
|
||||
|
||||
private start() {
|
||||
// We can't start the positioner without an anchor
|
||||
if (!this.anchorEl || !this.active) {
|
||||
if (!this.anchorEl || !this.active || !this.isConnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.popup.showPopover?.();
|
||||
this.popup?.showPopover?.();
|
||||
|
||||
this.cleanup = autoUpdate(this.anchorEl, this.popup, () => {
|
||||
this.reposition();
|
||||
@@ -299,7 +299,7 @@ export default class WaPopup extends WebAwesomeElement {
|
||||
|
||||
private async stop(): Promise<void> {
|
||||
return new Promise(resolve => {
|
||||
this.popup.hidePopover?.();
|
||||
this.popup?.hidePopover?.();
|
||||
|
||||
if (this.cleanup) {
|
||||
this.cleanup();
|
||||
@@ -317,7 +317,7 @@ export default class WaPopup extends WebAwesomeElement {
|
||||
/** Forces the popup to recalculate and reposition itself. */
|
||||
reposition() {
|
||||
// Nothing to do if the popup is inactive or the anchor doesn't exist
|
||||
if (!this.active || !this.anchorEl) {
|
||||
if (!this.active || !this.anchorEl || !this.popup) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -498,7 +498,7 @@ export default class WaPopup extends WebAwesomeElement {
|
||||
}
|
||||
|
||||
private updateHoverBridge = () => {
|
||||
if (this.hoverBridge && this.anchorEl) {
|
||||
if (this.hoverBridge && this.anchorEl && this.popup) {
|
||||
const anchorRect = this.anchorEl.getBoundingClientRect();
|
||||
const popupRect = this.popup.getBoundingClientRect();
|
||||
const isVertical = this.placement.includes('top') || this.placement.includes('bottom');
|
||||
|
||||
@@ -79,7 +79,7 @@ export default class WaTooltip extends WebAwesomeElement {
|
||||
/** The amount of time to wait before showing the tooltip when the user mouses in. */
|
||||
@property({ attribute: 'show-delay', type: Number }) showDelay = 150;
|
||||
|
||||
/** The amount of time to wait before hiding the tooltip when the user mouses out.. */
|
||||
/** The amount of time to wait before hiding the tooltip when the user mouses out. */
|
||||
@property({ attribute: 'hide-delay', type: Number }) hideDelay = 0;
|
||||
|
||||
/**
|
||||
|
||||
@@ -85,6 +85,10 @@ export default css`
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tree-item:not(.tree-item-has-expand-button):not(.tree-item-loading) .expand-button {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.tree-item-loading .expand-icon-slot wa-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -99,20 +99,24 @@ export class DraggableElement {
|
||||
|
||||
document.addEventListener('pointerup', this.handleDragStop);
|
||||
document.addEventListener('pointermove', this.handleDragMove);
|
||||
document.addEventListener('pointercancel', this.handleDragStop);
|
||||
document.addEventListener('touchend', this.handleDragStop);
|
||||
document.addEventListener('touchmove', this.handleDragMove);
|
||||
document.addEventListener('touchcancel', this.handleDragStop);
|
||||
this.options.start(clientX, clientY);
|
||||
};
|
||||
|
||||
private handleDragStop = (event: PointerEvent | TouchEvent) => {
|
||||
const clientX = 'touches' in event ? event.touches[0].clientX : (event as PointerEvent).clientX;
|
||||
const clientY = 'touches' in event ? event.touches[0].clientY : (event as PointerEvent).clientY;
|
||||
const clientX = 'changedTouches' in event ? event.changedTouches[0].clientX : (event as PointerEvent).clientX;
|
||||
const clientY = 'changedTouches' in event ? event.changedTouches[0].clientY : (event as PointerEvent).clientY;
|
||||
|
||||
this.isDragging = false;
|
||||
document.removeEventListener('pointerup', this.handleDragStop);
|
||||
document.removeEventListener('pointermove', this.handleDragMove);
|
||||
document.removeEventListener('pointercancel', this.handleDragStop);
|
||||
document.removeEventListener('touchend', this.handleDragStop);
|
||||
document.removeEventListener('touchmove', this.handleDragMove);
|
||||
document.removeEventListener('touchcancel', this.handleDragStop);
|
||||
this.options.stop(clientX, clientY);
|
||||
};
|
||||
|
||||
@@ -141,8 +145,10 @@ export class DraggableElement {
|
||||
public stop() {
|
||||
document.removeEventListener('pointerup', this.handleDragStop);
|
||||
document.removeEventListener('pointermove', this.handleDragMove);
|
||||
document.removeEventListener('pointercancel', this.handleDragStop);
|
||||
document.removeEventListener('touchend', this.handleDragStop);
|
||||
document.removeEventListener('touchmove', this.handleDragMove);
|
||||
document.removeEventListener('touchcancel', this.handleDragStop);
|
||||
this.element.removeEventListener('pointerdown', this.handleDragStart);
|
||||
if (supportsTouch) {
|
||||
this.element.removeEventListener('touchstart', this.handleDragStart);
|
||||
|
||||
@@ -1,4 +1,15 @@
|
||||
@layer wa-utilities {
|
||||
/* Apply Flexbox with 0 specificity to ensure an align-items util produces a visible change */
|
||||
:where(
|
||||
.wa-align-items-start,
|
||||
.wa-align-items-end,
|
||||
.wa-align-items-center,
|
||||
.wa-align-items-stretch,
|
||||
.wa-align-items-baseline
|
||||
) {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.wa-align-items-start {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,16 @@
|
||||
@layer wa-utilities {
|
||||
/* Apply Flexbox with 0 specificity to ensure a justify-content util produces a visible change */
|
||||
:where(
|
||||
.wa-justify-content-start,
|
||||
.wa-justify-content-end,
|
||||
.wa-justify-content-center,
|
||||
.wa-justify-content-space-around,
|
||||
.wa-justify-content-space-between,
|
||||
.wa-justify-content-space-evenly
|
||||
) {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.wa-justify-content-start {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user