Theme remixing: Dynamic code snippets (#729)

This commit is contained in:
Lea Verou
2025-02-07 23:03:34 -05:00
committed by GitHub
parent 9be7919c60
commit ffda52a7cf
7 changed files with 101 additions and 39 deletions

View File

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

View File

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

View File

@@ -34,9 +34,9 @@ wa_data.palettes = {
<iframe src='{{ page.url }}demo.html' id="demo"></iframe>
<wa-details id="mix_and_match" class="wa-gap-m" >
<h4 slot="summary" data-no-anchor data-no-outline>
<h4 slot="summary" data-no-anchor data-no-outline id="remix">
<wa-icon name="arrows-rotate"></wa-icon>
Remix
Remix this theme
<wa-icon id="what-is-remixing" href="#remixing" name="circle-question" slot="suffix" variant="regular"></wa-icon>
<wa-tooltip for="what-is-remixing">Customize this theme by changing its colors and/or remixing it with design elements from other themes!</wa-tooltip>
</h4>
@@ -132,31 +132,15 @@ wa_data.palettes = {
{% endblock %}
{% block afterContent %}
{% markdown %}
## How to use this theme
<h2 id="usage">How to use this theme</h2>
{% markdown %}
You can import this theme from the Web Awesome CDN.
{% set stylesheet = 'styles/themes/' + page.fileSlug + '.css' %}
{% include 'import-stylesheet-code.md.njk' %}
### Remixing { #remixing }
If you want to combine the **colors** from this theme with another theme, you can import this CSS file *after* the other themes CSS file:
{% set stylesheet = 'styles/themes/' + page.fileSlug + '/color.css' %}
{% include 'import-stylesheet-code.md.njk' %}
To use the **typography** from this theme with another theme, you can import this CSS file *after* the other themes CSS file:
{% set stylesheet = 'styles/themes/' + page.fileSlug + '/typography.css' %}
{% include 'import-stylesheet-code.md.njk' %}
<wa-callout variant="warning">
<wa-icon slot="icon" name="triangle-exclamation" variant="regular"></wa-icon>
Please note that not all combinations will look good — once youre mixing and matching, youre on your own!
</wa-callout>
## Dark mode
To activate the dark color scheme of the theme on any element and its contents, apply the class `wa-dark` to it.

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,37 @@
/**
* Get import code for remixed themes.
*/
export const urls = {
colors: id => `styles/themes/${id}/color.css`,
palette: id => `styles/color/${id}.css`,
brand: id => `styles/brand/${id}.css`,
typography: id => `styles/themes/${id}/typography.css`,
};
function getImport(url, options = {}) {
let { language = 'html', cdnUrl = '/dist/', attributes } = options;
url = cdnUrl + url;
if (language === 'css') {
return `@import url('${url}');`;
} else {
attributes = attributes ? ` ${attributes}` : '';
return `<link rel="stylesheet" href="${url}"${attributes} />`;
}
}
export function getCode(base, params, options) {
let ret = [];
if (base) {
ret.push(`styles/themes/${base}.css`);
}
ret.push(
...Object.entries(params)
.filter(([aspect, id]) => Boolean(id))
.map(([aspect, id]) => urls[aspect](id)),
);
return ret.map(url => getImport(url, options)).join('\n');
}

View File

@@ -11,7 +11,6 @@ eleventyComputed:
forceTheme: "{{ theme.fileSlug }}"
---
<link rel="stylesheet" href="/dist/styles/themes/{{ theme.fileSlug }}.css" />
<link rel="stylesheet" href="/docs/themes/showcase.css" />
{% set content %}
@@ -34,37 +33,45 @@ eleventyComputed:
</div>
</wa-image-comparer>
<script>
<script type="module">
import { getCode, urls as stylesheetURLs } from "/assets/scripts/remix.js";
function updateTheme() {
let params = new URLSearchParams(window.location.search);
let script = document.currentScript;
const stylesheetURLs = {
colors: id => `/dist/styles/themes/${ id }/color.css`,
palette: id => `/dist/styles/color/${ id }.css`,
brand: id => `/dist/styles/brand/${ id }.css`,
typography: id => `/dist/styles/themes/${ id }/typography.css`
params = Object.fromEntries(params.entries());
const docsURLs = {
colors: '/docs/themes/',
palette: '/docs/palettes/',
typography: '/docs/themes/'
};
const icons = {
colors: 'palette',
palette: 'swatchbook',
brand: 'droplet',
typography: 'font-case'
}
};
for (let link of document.querySelectorAll('link.mix-and-match')) {
link.remove();
}
let msgs = [];
let code = getCode("{{ theme.fileSlug }}", params, {attributes: 'class="mix-and-match"'});
document.head.insertAdjacentHTML('beforeend', code);
for (let name in stylesheetURLs) {
if (params.get(name)) {
let url = stylesheetURLs[name](params.get(name));
script.insertAdjacentHTML('beforebegin', `<link rel="stylesheet" href="${ url }" class="mix-and-match" />`);
let docsURL = (name === 'palette' ? '/docs/palettes/' : '/docs/themes/') + params.get(name) + '/';
let title = params.get(name).replace(/^[a-z]/g, c => c.toUpperCase());
let override = params[name];
if (override) {
let docsURL = docsURLs[name] ? docsURLs[name] + override + '/' : '';
let title = override.replace(/^[a-z]/g, c => c.toUpperCase());
if (docsURL) {
title = `<a href="${ docsURL }">${ title }</a>`;
}
let icon = icons[name];
msgs.push(`<wa-icon name="${icon}" variant="regular"></wa-icon> <a href="${ docsURL }">${ title }</a>`);
msgs.push(`<wa-icon name="${icon}" variant="regular"></wa-icon> ${ title }`);
}
}

View File

@@ -1,7 +1,16 @@
import { getCode } from '/assets/scripts/remix.js';
await Promise.all(['wa-select', 'wa-option', 'wa-details'].map(tag => customElements.whenDefined(tag)));
globalThis.Prism = globalThis.Prism || {};
globalThis.Prism.manual = true;
await import('/assets/scripts/prism.js');
Prism.plugins.customClass.prefix('code-');
const cdnUrl = document.documentElement.dataset.cdnUrl;
const domChange = document.startViewTransition ? document.startViewTransition.bind(document) : fn => fn();
let selects, data;
let selects, data, codeSnippets;
let computed = {
get isRemixed() {
@@ -25,6 +34,12 @@ function init() {
[...document.querySelectorAll('#mix_and_match wa-select')].map(select => [select.getAttribute('name'), select]),
);
codeSnippets = document.querySelector('#usage ~ wa-tab-group.import-stylesheet-code:first-of-type');
codeSnippets = {
html: codeSnippets.querySelector('code.language-html'),
css: codeSnippets.querySelector('code.language-css'),
};
data = {
baseTheme: wa_data.baseTheme,
themes: wa_data.themes,
@@ -66,7 +81,7 @@ function init() {
Promise.all(Object.values(selects).map(select => select.updateComplete)).then(() => render());
return { selects, data, computed, render };
return { selects, codeSnippets, data, computed, render };
}
globalThis.remixApp = init();
@@ -139,6 +154,16 @@ function render(changedAspect) {
// We dont want to clog the users history while they iterate
let historyAction = location.search ? 'replaceState' : 'pushState';
history[historyAction](null, '', `?${data.urlParams}`);
// Update code snippets
for (let language in codeSnippets) {
let codeSnippet = codeSnippets[language];
let copyButton = codeSnippet.previousElementSibling;
let code = getCode(data.baseTheme, data.params, { language, cdnUrl });
codeSnippet.textContent = code;
copyButton.value = code;
Prism.highlightElement(codeSnippet);
}
}
addEventListener('turbo:render', event => {