mirror of
https://github.com/shoelace-style/webawesome.git
synced 2026-01-12 04:09:12 +00:00
Improve theme remixing UI
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
{%- if not stylesheets %}{% set stylesheets = [stylesheet] %}{% endif -%}
|
||||
|
||||
<wa-tab-group>
|
||||
<wa-tab panel="html">In HTML</wa-tab>
|
||||
<wa-tab panel="css">In CSS</wa-tab>
|
||||
@@ -5,14 +7,18 @@
|
||||
|
||||
Simply add the following code to the `<head>` of your page:
|
||||
```html
|
||||
<link rel="stylesheet" href="{% cdnUrl stylesheet %}" />
|
||||
{% for stylesheet in stylesheets -%}
|
||||
<link rel="stylesheet" href="{% cdnUrl stylesheet %}" />{% if not loop.last %}
|
||||
{% endif %}{% endfor %}
|
||||
```
|
||||
</wa-tab-panel>
|
||||
<wa-tab-panel name="css">
|
||||
|
||||
Simply add the following code at the top of your CSS file:
|
||||
```css
|
||||
@import url('{% cdnUrl stylesheet %}');
|
||||
{% for stylesheet in stylesheets -%}
|
||||
@import url('{% cdnUrl stylesheet %}');{% if not loop.last %}
|
||||
{% endif %}{% endfor %}
|
||||
```
|
||||
</wa-tab-panel>
|
||||
</wa-tab-group>
|
||||
|
||||
@@ -3,64 +3,15 @@
|
||||
{# {% set forceTheme = page.fileSlug %} #}
|
||||
|
||||
{% extends '../_includes/base.njk' %}
|
||||
{% set themeId = page.fileSlug %}
|
||||
{% if themeId == 'remixed' -%}
|
||||
{% set themeId = 'default' %}
|
||||
{% endif -%}
|
||||
|
||||
{% block header %}
|
||||
<iframe src='{{ page.url }}demo.html' id="demo"></iframe>
|
||||
|
||||
<p id="mix_and_match" class="wa-gap-m">
|
||||
<strong>
|
||||
<wa-icon name="merge" slot="prefix"></wa-icon>
|
||||
Remix
|
||||
<wa-icon-button href="#remixing" name="circle-question" slot="suffix" variant="regular" label="How to use?"></wa-icon-button>
|
||||
</strong>
|
||||
<wa-select name="colors" label="Colors from…" size="small">
|
||||
<wa-icon name="palette" slot="prefix" variant="regular"></wa-icon>
|
||||
<wa-option value="">(Theme default)</wa-option>
|
||||
<wa-divider></wa-divider>
|
||||
{% for theme in collections.theme | sort %}
|
||||
{% if theme.fileSlug !== page.fileSlug %}
|
||||
<wa-option value="{{ theme.fileSlug }}">{{ theme.data.title }}</wa-option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</wa-select>
|
||||
|
||||
<wa-select name="palette" label="Palette" size="small">
|
||||
<wa-icon name="swatchbook" slot="prefix" variant="regular"></wa-icon>
|
||||
<wa-option value="">(Theme default)</wa-option>
|
||||
<wa-divider></wa-divider>
|
||||
{% for p in collections.palette | sort %}
|
||||
{% if p.fileSlug !== palette %}
|
||||
<wa-option value="{{ p.fileSlug }}">{{ p.data.title }}</wa-option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</wa-select>
|
||||
|
||||
<wa-select name="typography" label="Typography from…" size="small">
|
||||
<wa-icon name="font-case" slot="prefix"></wa-icon>
|
||||
<wa-option value="">(Theme default)</wa-option>
|
||||
<wa-divider></wa-divider>
|
||||
{% for theme in collections.theme | sort %}
|
||||
{% if theme.fileSlug !== page.fileSlug %}
|
||||
<wa-option value="{{ theme.fileSlug }}">{{ theme.data.title }}</wa-option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</wa-select>
|
||||
|
||||
|
||||
</p>
|
||||
<script>
|
||||
document.querySelector('#mix_and_match').addEventListener('change', function(event) {
|
||||
let selects = document.querySelectorAll('#mix_and_match wa-select');
|
||||
let url = new URL(demo.src);
|
||||
|
||||
for (let select of selects) {
|
||||
url.searchParams.set(select.name, select.value);
|
||||
}
|
||||
|
||||
demo.src = url;
|
||||
});
|
||||
</script>
|
||||
<iframe src='/docs/themes/{{ themeId }}/demo.html' id="demo"></iframe>
|
||||
|
||||
{% if palette -%}
|
||||
<h2>Default Color Palette</h2>
|
||||
{% set paletteURL = '/docs/palettes/' + palette + '/' %}
|
||||
{% set themePage = page %}
|
||||
@@ -69,34 +20,20 @@
|
||||
{% include 'page-card.njk' %}
|
||||
</div>
|
||||
{% set page = themePage %}
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block afterContent %}
|
||||
{% markdown %}
|
||||
{%- if page.fileSlug != 'remixed' %}
|
||||
## How to use this theme
|
||||
|
||||
You can import this theme from the Web Awesome CDN.
|
||||
|
||||
{% set stylesheet = 'styles/themes/' + page.fileSlug + '.css' %}
|
||||
{% set stylesheet = 'styles/themes/' + themeId + '.css' %}
|
||||
{% include 'import-stylesheet-code.md.njk' %}
|
||||
|
||||
### Remixing { #remixing }
|
||||
|
||||
If you want to combine the **colors** from this theme with another theme, you can import this CSS file *after* the other theme’s CSS file:
|
||||
|
||||
{% set stylesheet = 'styles/themes/' + page.fileSlug + '/color.css' %}
|
||||
{% include 'import-stylesheet-code.md.njk' %}
|
||||
|
||||
To use the **typography** from this theme with another theme, you can import this CSS file *after* the other theme’s CSS file:
|
||||
|
||||
{% set stylesheet = 'styles/themes/' + page.fileSlug + '/typography.css' %}
|
||||
{% include 'import-stylesheet-code.md.njk' %}
|
||||
|
||||
<wa-callout variant="warning">
|
||||
<wa-icon slot="icon" name="triangle-exclamation" variant="regular"></wa-icon>
|
||||
Please note that not all combinations will look good — once you’re mixing and matching, you’re on your own!
|
||||
</wa-callout>
|
||||
{% endif %}
|
||||
|
||||
## Dark mode
|
||||
|
||||
|
||||
@@ -538,23 +538,4 @@ table.colors {
|
||||
height: 65vh;
|
||||
max-height: 21lh;
|
||||
}
|
||||
|
||||
#mix_and_match {
|
||||
strong {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--wa-space-2xs);
|
||||
margin-top: 1.2em;
|
||||
}
|
||||
|
||||
wa-select::part(label) {
|
||||
margin-block-end: 0;
|
||||
}
|
||||
|
||||
wa-select[value='']::part(display-input),
|
||||
wa-option[value=''] {
|
||||
font-style: italic;
|
||||
color: var(--wa-color-text-quiet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
17
docs/docs/themes/demo.njk
vendored
17
docs/docs/themes/demo.njk
vendored
@@ -19,6 +19,10 @@ eleventyComputed:
|
||||
{% include 'breadcrumbs.njk' %}
|
||||
<h1 class="title">{{ theme.data.title }}</h1>
|
||||
<p id="mix_and_match" hidden class="wa-size-s"></p>
|
||||
<wa-button href="/docs/themes/remixed/?base={{ theme.fileSlug }}" class="remix-link" size="small" variant="brand" target="_parent">
|
||||
<wa-icon name="merge"></wa-icon>
|
||||
Remix
|
||||
</wa-button>
|
||||
<p>{% include 'status.njk' %}</p>
|
||||
<p id="theme-showcase-description">{{ theme.data.description | inlineMarkdown | safe }}</p>
|
||||
</header>
|
||||
@@ -39,12 +43,12 @@ function updateTheme() {
|
||||
let params = new URLSearchParams(window.location.search);
|
||||
let script = document.currentScript;
|
||||
const stylesheetURLs = {
|
||||
colors: id => `/dist/styles/themes/${ id }/color.css`,
|
||||
color: id => `/dist/styles/themes/${ id }/color.css`,
|
||||
palette: id => `/dist/styles/color/${ id }.css`,
|
||||
typography: id => `/dist/styles/themes/${ id }/typography.css`
|
||||
};
|
||||
const icons = {
|
||||
colors: 'palette',
|
||||
color: 'palette',
|
||||
palette: 'swatchbook',
|
||||
typography: 'font-case'
|
||||
}
|
||||
@@ -66,11 +70,14 @@ function updateTheme() {
|
||||
}
|
||||
}
|
||||
|
||||
let isRemixed = msgs.length > 0;
|
||||
document.documentElement.classList.toggle("remixed", isRemixed);
|
||||
|
||||
for (let p of mix_and_match) {
|
||||
p.hidden = msgs.length === 0;
|
||||
if (msgs.length) {
|
||||
p.hidden = !isRemixed;
|
||||
if (isRemixed) {
|
||||
let icon =
|
||||
p.innerHTML = `<strong><wa-icon name="merge"></wa-icon> Remixed</strong> ` + msgs.map(msg => `<wa-badge appearance=outlined>
|
||||
p.innerHTML = `<strong><wa-icon name="merge"></wa-icon> Remixed</strong><br> ` + msgs.map(msg => `<wa-badge appearance=outlined>
|
||||
${ msg }</wa-badge>`).join(' ');
|
||||
}
|
||||
}
|
||||
|
||||
138
docs/docs/themes/remixed/index.js
vendored
Normal file
138
docs/docs/themes/remixed/index.js
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
let params = { base: 'default', palette: '', color: '', typography: '' };
|
||||
|
||||
// Find usage code snippet and prepare it
|
||||
let snippets = document.querySelectorAll('#remixed-usage ~ wa-tab-group pre > code');
|
||||
let copyButtons = document.querySelectorAll('#remixed-usage ~ wa-tab-group pre > wa-copy-button');
|
||||
let codeExamples = [];
|
||||
|
||||
for (let snippet of snippets) {
|
||||
let tokens = [...snippet.children];
|
||||
let base = tokens.shift();
|
||||
let [palette, color, typography] = tokens;
|
||||
|
||||
codeExamples.push({ snippet, base, palette, color, typography });
|
||||
|
||||
// Remove non-base tokens
|
||||
for (let token of tokens) {
|
||||
let whitespace = token.previousSibling;
|
||||
|
||||
if (whitespace.nodeType === Node.TEXT_NODE) {
|
||||
// Move whitespace to beginning of node
|
||||
token.prepend(token.previousSibling);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read URL params and apply them. This facilitates permalinks.
|
||||
if (location.search) {
|
||||
let urlParams = new URLSearchParams(location.search);
|
||||
|
||||
for (let aspect in params) {
|
||||
if (urlParams.has(aspect)) {
|
||||
params[aspect] = urlParams.get(aspect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const selects = Object.fromEntries(
|
||||
[...document.querySelectorAll('#mix_and_match wa-select')].map(select => [select.getAttribute('name'), select]),
|
||||
);
|
||||
|
||||
document.querySelector('#mix_and_match').addEventListener(
|
||||
'change',
|
||||
function (event) {
|
||||
for (let name in selects) {
|
||||
params[name] = selects[name].value;
|
||||
}
|
||||
|
||||
render();
|
||||
},
|
||||
{ capture: true },
|
||||
);
|
||||
|
||||
function hasOverride(name) {
|
||||
if (!params[name]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (name === 'palette') {
|
||||
return params[name] !== defaultPalettes[params.base];
|
||||
}
|
||||
|
||||
if (name !== 'base') {
|
||||
return params[name] !== params.base;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function render() {
|
||||
let demoUrl = new URL(`/docs/themes/${params.base}/demo.html`, location);
|
||||
let pageParams = new URLSearchParams(params);
|
||||
|
||||
for (let aspect in params) {
|
||||
if (aspect !== 'base' && params[aspect]) {
|
||||
demoUrl.searchParams.set(aspect, params[aspect]);
|
||||
}
|
||||
|
||||
if (!params[aspect] || (aspect === 'base' && params[aspect] === 'default') || !hasOverride(aspect)) {
|
||||
pageParams.delete(aspect);
|
||||
}
|
||||
|
||||
// Output code snippet
|
||||
if (aspect !== 'base') {
|
||||
for (let codeExample of codeExamples) {
|
||||
let token = codeExample[aspect];
|
||||
let value = params[aspect];
|
||||
|
||||
if (hasOverride(aspect)) {
|
||||
// Update code example
|
||||
let valueToken = [...token.querySelectorAll('.code-attr-value, .code-url')].pop();
|
||||
valueToken.textContent = replaceStyleSheetURL(valueToken.textContent, aspect, value);
|
||||
|
||||
// Add code example to <pre>
|
||||
codeExample.snippet.append(token);
|
||||
} else {
|
||||
token.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update selects
|
||||
if (selects[aspect].value === undefined) {
|
||||
selects[aspect].setAttribute('value', params[aspect]);
|
||||
} else {
|
||||
selects[aspect].value = params[aspect];
|
||||
}
|
||||
}
|
||||
|
||||
// Update code snippet copy buttons
|
||||
for (let copyButton of copyButtons) {
|
||||
copyButton.value = copyButton.nextElementSibling.textContent;
|
||||
}
|
||||
|
||||
// Update demo URL
|
||||
demo.src = demoUrl;
|
||||
|
||||
// Update page URL. If there’s already a search, replace it.
|
||||
// We don’t want to clog the user’s history while they iterate
|
||||
let historyAction = location.search ? 'replaceState' : 'pushState';
|
||||
history[historyAction](null, '', `?${pageParams}`);
|
||||
}
|
||||
|
||||
const regexes = {
|
||||
base: /\/themes\/([a-z-]+)\.css/,
|
||||
palette: /\/color\/([a-z-]+)\.css/,
|
||||
color: /\/themes\/([a-z-]+)\/color\.css/,
|
||||
typography: /\/themes\/([a-z-]+)\/typography\.css/,
|
||||
};
|
||||
|
||||
function replaceStyleSheetURL(url, name, value) {
|
||||
let regex = regexes[name];
|
||||
return url.replace(regex, (match, oldValue) => {
|
||||
return match.replace(oldValue, value);
|
||||
});
|
||||
}
|
||||
|
||||
globalThis.params = params;
|
||||
render();
|
||||
84
docs/docs/themes/remixed/index.njk
vendored
Normal file
84
docs/docs/themes/remixed/index.njk
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
---
|
||||
title: Remixed
|
||||
description: TODO
|
||||
isPro: true
|
||||
tags: pro
|
||||
---
|
||||
<link rel="stylesheet" href="{{ page.url }}/style.css">
|
||||
{% block header %}
|
||||
<script>
|
||||
globalThis.defaultPalettes = {
|
||||
{% for theme in collections.theme | sort -%}
|
||||
"{{ theme.fileSlug }}": "{{ theme.data.palette }}",
|
||||
{%- endfor %}
|
||||
};
|
||||
</script>
|
||||
<p id="mix_and_match" class="wa-gap-m">
|
||||
{# <strong>
|
||||
<wa-icon name="merge" slot="prefix"></wa-icon>
|
||||
Remix
|
||||
<wa-icon-button href="#remixing" name="circle-question" slot="suffix" variant="regular" label="How to use?"></wa-icon-button>
|
||||
</strong> #}
|
||||
<wa-select name="base" label="Base:" size="small" value="default">
|
||||
<wa-icon name="brush" slot="prefix" variant="regular"></wa-icon>
|
||||
{% for theme in collections.theme | sort %}
|
||||
{% if theme.fileSlug !== page.fileSlug %}
|
||||
<wa-option value="{{ theme.fileSlug }}" data-palette="{{ theme.data.palette }}">{{ theme.data.title }}</wa-option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</wa-select>
|
||||
|
||||
<wa-select name="palette" label="Palette" size="small">
|
||||
<wa-icon name="swatchbook" slot="prefix" variant="regular"></wa-icon>
|
||||
<wa-option value="">(Theme default)</wa-option>
|
||||
<wa-divider></wa-divider>
|
||||
{% for p in collections.palette | sort %}
|
||||
<wa-option value="{{ p.fileSlug }}">{{ p.data.title }}</wa-option>
|
||||
{% endfor %}
|
||||
</wa-select>
|
||||
|
||||
<wa-select name="color" label="Colors from…" size="small" value="">
|
||||
<wa-icon name="palette" slot="prefix" variant="regular"></wa-icon>
|
||||
<wa-option value="">(Theme default)</wa-option>
|
||||
<wa-divider></wa-divider>
|
||||
{% for theme in collections.theme | sort %}
|
||||
<wa-option value="{{ theme.fileSlug }}">{{ theme.data.title }}</wa-option>
|
||||
{% endfor %}
|
||||
</wa-select>
|
||||
|
||||
<wa-select name="typography" label="Typography from…" size="small" value="">
|
||||
<wa-icon name="font-case" slot="prefix"></wa-icon>
|
||||
<wa-option value="">(Theme default)</wa-option>
|
||||
<wa-divider></wa-divider>
|
||||
{% for theme in collections.theme | sort %}
|
||||
<wa-option value="{{ theme.fileSlug }}">{{ theme.data.title }}</wa-option>
|
||||
{% endfor %}
|
||||
</wa-select>
|
||||
</p>
|
||||
<script src="{{ page.url }}index.js" type="module"></script>
|
||||
|
||||
<wa-callout variant="warning">
|
||||
<wa-icon slot="icon" name="triangle-exclamation" variant="regular"></wa-icon>
|
||||
Please note that not all combinations will look good — once you’re mixing and matching, you’re on your own!
|
||||
</wa-callout>
|
||||
{% endblock %}
|
||||
|
||||
{% block afterContent %}
|
||||
{% markdown %}
|
||||
|
||||
## How to use your remixed theme { #remixed-usage }
|
||||
|
||||
You can import your remixed theme by importing the individual theme files from the Web Awesome CDN.
|
||||
{# Note: If you change the order here, you need to update index.js too #}
|
||||
{% set stylesheets = [
|
||||
'styles/themes/default.css',
|
||||
'styles/color/default.css',
|
||||
'styles/themes/default/color.css',
|
||||
'styles/themes/default/typography.css'
|
||||
] %}
|
||||
{% set stylesheet = 'styles/themes/default/color.css' %}
|
||||
{% include 'import-stylesheet-code.md.njk' %}
|
||||
|
||||
|
||||
{% endmarkdown %}
|
||||
{% endblock %}
|
||||
18
docs/docs/themes/remixed/style.css
vendored
Normal file
18
docs/docs/themes/remixed/style.css
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
#mix_and_match {
|
||||
strong {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--wa-space-2xs);
|
||||
margin-top: 1.2em;
|
||||
}
|
||||
|
||||
wa-select::part(label) {
|
||||
margin-block-end: 0;
|
||||
}
|
||||
|
||||
wa-select[value='']::part(display-input),
|
||||
wa-option[value=''] {
|
||||
font-style: italic;
|
||||
color: var(--wa-color-text-quiet);
|
||||
}
|
||||
}
|
||||
12
docs/docs/themes/showcase.css
vendored
12
docs/docs/themes/showcase.css
vendored
@@ -9,6 +9,18 @@ body,
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
html.remixed {
|
||||
.remix-link {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
html:not(.remixed) {
|
||||
#mix_and_match {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#mix_and_match {
|
||||
font-weight: var(--wa-font-weight-semibold);
|
||||
color: var(--wa-color-text-quiet);
|
||||
|
||||
Reference in New Issue
Block a user