diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 480ad4982..77de2d839 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -3,6 +3,7 @@ "dbaeumer.vscode-eslint", "esbenp.prettier-vscode", "bierner.lit-html", - "streetsidesoftware.code-spell-checker" + "streetsidesoftware.code-spell-checker", + "ronnidc.nunjucks" ] } diff --git a/cspell.json b/cspell.json index f6f397bb8..b9dbf3e9a 100644 --- a/cspell.json +++ b/cspell.json @@ -8,8 +8,10 @@ "APG", "apos", "atrule", + "autocapitalize", "autocorrect", "autofix", + "autofocus", "autoload", "autoloader", "autoloading", @@ -43,16 +45,22 @@ "csspart", "cssproperty", "cssstate", + "datalist", "datetime", "describedby", + "dictsort", "Docsify", "dogfood", "dropdowns", "easings", "ecommerce", + "eleventy", + "elif", "endfor", + "endmarkdown", "endraw", "endregion", + "endset", "enterkeyhint", "eqeqeq", "erroneou", @@ -60,6 +68,7 @@ "esbuild", "exportmaps", "exportparts", + "fetchpriority", "fieldsets", "focusin", "focusout", @@ -78,6 +87,7 @@ "giga", "globby", "Grayscale", + "groupby", "haspopup", "heroicons", "hexa", @@ -88,6 +98,7 @@ "inputmode", "ionicon", "ionicons", + "jank", "jsDelivr", "jsfiddle", "keydown", @@ -123,7 +134,9 @@ "noindex", "noopener", "noreferrer", + "noscript", "novalidate", + "nowrap", "Numberish", "nums", "oklab", @@ -159,6 +172,7 @@ "scroller", "Scrollers", "Segoe", + "selectattr", "semibold", "shadowrootmode", "Shortcode", @@ -192,6 +206,7 @@ "typeof", "unbundles", "unbundling", + "Uncategorized", "unicons", "unsanitized", "unsupportive", diff --git a/packages/webawesome/docs/.eleventy.js b/packages/webawesome/docs/.eleventy.js index 48b2361c1..90e035288 100644 --- a/packages/webawesome/docs/.eleventy.js +++ b/packages/webawesome/docs/.eleventy.js @@ -1,66 +1,98 @@ import * as fs from 'node:fs'; import * as path from 'node:path'; +import { parse } from 'path'; import { anchorHeadingsPlugin } from './_utils/anchor-headings.js'; import { codeExamplesPlugin } from './_utils/code-examples.js'; import { copyCodePlugin } from './_utils/copy-code.js'; import { currentLink } from './_utils/current-link.js'; import { highlightCodePlugin } from './_utils/highlight-code.js'; +import { getComponents } from './_utils/manifest.js'; import { markdown } from './_utils/markdown.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 process from 'process'; +import * as url from 'url'; import { outlinePlugin } from './_utils/outline.js'; import { replaceTextPlugin } from './_utils/replace-text.js'; 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 isDev = process.argv.includes('--develop'); +const packageData = JSON.parse(await readFile(path.join(__dirname, '..', 'package.json'), 'utf-8')); +const docsDir = path.join(process.env.BASE_DIR || '.', 'docs'); +const passThroughExtensions = ['js', 'css', 'png', 'svg', 'jpg', 'mp4']; +const allComponents = getComponents(); -const globalData = { - package: packageData, - layout: 'page.njk', - server: { +/** + * If you plan to add or remove any of these extensions, make sure to let either Konnor or Cory know as these + * passthrough extensions will also need to be updated in the Web Awesome App. + */ +const passThrough = [...passThroughExtensions.map(ext => path.join(docsDir, '**/*.' + ext))]; + +/** + * This is the guard we use for now to make sure our final built files don't 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'; + +export default async function (eleventyConfig) { + // + // Set all global template data here + // + eleventyConfig.addGlobalData('package', packageData); + eleventyConfig.addGlobalData('layout', 'page.njk'); + eleventyConfig.addGlobalData('server', { head: '', loginOrAvatar: '', flashes: '', - }, -}; - -export default async function (eleventyConfig) { - /** - * If you plan to add or remove any of these extensions, make sure to let either Konnor or Cory know as these passthrough extensions - * will also need to be updated in the Web Awesome App. - */ - const passThroughExtensions = ['js', 'css', 'png', 'svg', 'jpg', 'mp4']; - - const docsDir = path.join(process.env.BASE_DIR || '.', 'docs'); - const passThrough = [...passThroughExtensions.map(ext => path.join(docsDir, '**/*.' + ext))]; - - /** - * 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'; - - // Add template data - for (let name in globalData) { - eleventyConfig.addGlobalData(name, globalData[name]); - } + }); // Template filters - {{ content | filter }} eleventyConfig.addFilter('inlineMarkdown', content => markdown.renderInline(content || '')); eleventyConfig.addFilter('markdown', content => markdown.render(content || '')); + eleventyConfig.addFilter('stripExtension', string => parse(string + '').name); + eleventyConfig.addFilter('stripPrefix', content => content.replace(/^wa-/, '')); + // Trims whitespace and pipes from the start and end of a string. Useful for CEM types, which can be pipe-delimited. + // With Prettier 3, this means a leading pipe will exist be present when the line wraps. + eleventyConfig.addFilter('trimPipes', content => { + return typeof content === 'string' ? content.replace(/^(\s|\|)/g, '').replace(/(\s|\|)$/g, '') : content; + }); - for (let name in filters) { - eleventyConfig.addFilter(name, filters[name]); - } + // Custom filter to sort with a priority item first, e.g. + // {{ collection | sortWithFirst('fileSlug', 'default') }} => the item with the fileSlug of 'default' will be first + eleventyConfig.addFilter('sortWithFirst', function (collection, property, firstValue) { + const items = [...collection]; // Create a copy to avoid mutating original + return items.sort((a, b) => { + const aValue = property ? a[property] : a; + const bValue = property ? b[property] : b; + if (aValue === firstValue) return -1; + if (bValue === firstValue) return 1; + return 0; + }); + }); + + // + // Add the componentPages collection + // + eleventyConfig.addCollection('componentPages', function (collectionApi) { + const componentPages = collectionApi.getFilteredByGlob( + path.join(eleventyConfig.directories.input, 'docs/components/**/*.md'), + ); + + return componentPages.map(page => { + const componentName = path.basename(page.inputPath, '.md'); + const tagName = `wa-${componentName}`; + const component = allComponents.find(c => c.tagName === tagName); + + // Add component to the page's data + if (component) { + page.data.component = component; + } + + return page; + }); + }); // Shortcodes - {% shortCode arg1, arg2 %} eleventyConfig.addShortcode('cdnUrl', location => { @@ -102,6 +134,15 @@ export default async function (eleventyConfig) { eleventyConfig.addPairedShortcode('markdown', content => markdown.render(content || '')); // Helpers + eleventyConfig.addNunjucksGlobal('getComponent', tagName => { + const component = allComponents.find(c => c.tagName === tagName); + if (!component) { + throw new Error( + `Unable to find "<${tagName}>". Make sure the file name is the same as the tag name (without prefix).`, + ); + } + return component; + }); // Use our own markdown instance eleventyConfig.setLibrary('md', markdown); @@ -184,7 +225,8 @@ export default async function (eleventyConfig) { } // // SSR plugin - // // Make sure this is the last thing, we don't want to run the risk of accidentally transforming shadow roots with the nunjucks 2nd transform. + // // Make sure this is the last thing, we don't want to run the risk of accidentally transforming shadow roots with + // // the nunjucks 2nd transform. // if (!isDev) { // // // // Problematic components in SSR land: diff --git a/packages/webawesome/docs/_data/componentList.js b/packages/webawesome/docs/_data/componentList.js deleted file mode 100644 index b212e5738..000000000 --- a/packages/webawesome/docs/_data/componentList.js +++ /dev/null @@ -1,78 +0,0 @@ -/** - * @module components Fetches components from custom-elements.json and exposes them in a saner format. - */ -import { readFileSync } from 'fs'; -import { dirname, join, resolve } from 'path'; -import { fileURLToPath } from 'url'; - -const __dirname = dirname(fileURLToPath(import.meta.url)); -const customElementsJSON = process.env.DIST_DIR - ? join(process.env.DIST_DIR, 'custom-elements.json') - : resolve(__dirname, '../../dist/custom-elements.json'); - -const manifest = JSON.parse(readFileSync(customElementsJSON), 'utf-8'); - -const components = manifest.modules.flatMap(module => { - return module.declarations - .filter(c => c?.customElement) - .map(declaration => { - // Generate the dist path based on the src path and attach it to the component - declaration.path = module.path.replace(/^src\//, 'dist/').replace(/\.ts$/, '.js'); - - // Remove private members and those that lack a description - const members = declaration.members?.filter(member => member.description && member.privacy !== 'private'); - - const methods = members?.filter(prop => prop.kind === 'method' && prop.privacy !== 'private'); - const attributes = declaration.attributes ?? []; - const properties = members?.filter(prop => { - // Look for a corresponding attribute - const attribute = attributes?.find(attr => attr.fieldName === prop.name); - if (attribute) { - prop.attribute = attribute.name || attribute.fieldName; - } - - return prop.kind === 'field' && prop.privacy !== 'private'; - }); - - return { - ...declaration, - slug: declaration.tagName.replace(/^wa-/, ''), - methods, - attributes, - properties, - }; - }); -}); - -// Build dependency graphs -components.forEach(component => { - const dependencies = []; - - // Recursively fetch sub-dependencies - function getDependencies(tag) { - const cmp = components.find(c => c.tagName === tag); - if (!cmp || !Array.isArray(component.dependencies)) { - return; - } - - cmp.dependencies?.forEach(dependentTag => { - if (!dependencies.includes(dependentTag)) { - dependencies.push(dependentTag); - } - getDependencies(dependentTag); - }); - } - - getDependencies(component.tagName); - - component.dependencies = dependencies.sort(); -}); - -// Sort by name -components.sort((a, b) => { - if (a.name < b.name) return -1; - if (a.name > b.name) return 1; - return 0; -}); - -export default components; diff --git a/packages/webawesome/docs/_data/components.js b/packages/webawesome/docs/_data/components.js deleted file mode 100644 index 1dab4c111..000000000 --- a/packages/webawesome/docs/_data/components.js +++ /dev/null @@ -1,3 +0,0 @@ -import componentList from './componentList.js'; - -export default Object.fromEntries(componentList.map(component => [component.slug, component])); diff --git a/packages/webawesome/docs/_data/hueRanges.js b/packages/webawesome/docs/_data/hueRanges.js deleted file mode 100644 index 63d7400a6..000000000 --- a/packages/webawesome/docs/_data/hueRanges.js +++ /dev/null @@ -1 +0,0 @@ -export { hueRanges as default } from '../assets/data/index.js'; diff --git a/packages/webawesome/docs/_data/hues.json b/packages/webawesome/docs/_data/hues.json deleted file mode 100644 index 2e68ebe4b..000000000 --- a/packages/webawesome/docs/_data/hues.json +++ /dev/null @@ -1 +0,0 @@ -["red", "orange", "yellow", "green", "cyan", "blue", "indigo", "purple", "pink", "gray"] diff --git a/packages/webawesome/docs/_data/palettes.js b/packages/webawesome/docs/_data/palettes.js deleted file mode 100644 index 3ce705beb..000000000 --- a/packages/webawesome/docs/_data/palettes.js +++ /dev/null @@ -1 +0,0 @@ -export { default as default } from '../../src/styles/color/scripts/palettes-analyzed.js'; diff --git a/packages/webawesome/docs/_data/themer.js b/packages/webawesome/docs/_data/themer.js new file mode 100644 index 000000000..582822f04 --- /dev/null +++ b/packages/webawesome/docs/_data/themer.js @@ -0,0 +1,735 @@ +/** + * All themes used in the themer. + */ +export const themes = [ + { + // + // #region Default + // + name: 'Default', + description: 'Your trusty companion, like a perfectly broken-in pair of jeans.', + filename: 'default.css', + isPro: false, + fonts: { + body: { + name: 'OS Default', + css: 'ui-sans-serif, system-ui, sans-serif', + href: null, + }, + heading: { + name: 'OS Default', + css: 'ui-sans-serif, system-ui, sans-serif', + href: null, + }, + code: { + name: 'OS Default', + css: 'ui-monospace, monospace', + href: null, + }, + longform: { + name: 'OS Default', + css: 'ui-serif, serif', + href: null, + }, + }, + icons: { + family: 'classic', + weight: 1, + }, + palette: { + name: 'Default', + filename: 'default.css', + }, + colorBrand: { + color: 'blue', + }, + tokens: { + // Fonts + '--wa-font-family-body': 'ui-sans-serif, system-ui, sans-serif', + '--wa-font-family-heading': 'var(--wa-font-family-body)', + '--wa-font-family-code': 'ui-monospace, monospace', + '--wa-font-family-longform': 'ui-serif, serif', + '--wa-font-weight-body': 400, + '--wa-font-weight-heading': 600, + '--wa-font-weight-code': 400, + '--wa-font-weight-longform': 400, + + // Elements + '--wa-border-radius-scale': 1, + '--wa-space-scale': 1, + '--wa-border-width-scale': 1, + }, + }, + // #endregion + + // + // #region Awesome + // + { + name: 'Awesome', + description: 'Punchy and vibrant, the rock star of themes.', + filename: 'awesome.css', + isPro: false, + fonts: { + body: { + name: 'Quicksand', + css: 'Quicksand, sans-serif', + href: 'https://fonts.googleapis.com/css2?family=Crimson+Pro:ital,wght@0,200..900;1,200..900&family=Quicksand:wght@300..700&display=swap', + }, + heading: { + name: 'Quicksand', + css: 'Quicksand, sans-serif', + href: 'https://fonts.googleapis.com/css2?family=Crimson+Pro:ital,wght@0,200..900;1,200..900&family=Quicksand:wght@300..700&display=swap', + }, + code: { + name: 'OS Default', + css: 'ui-monospace, monospace', + href: null, + }, + longform: { + name: 'Crimson Pro', + css: '"Crimson Pro", serif', + href: 'https://fonts.googleapis.com/css2?family=Crimson+Pro:ital,wght@0,200..900;1,200..900&family=Quicksand:wght@300..700&display=swap', + }, + }, + icons: { + family: 'classic', + weight: 2, + }, + palette: { + name: 'Bright', + filename: 'bright.css', + }, + colorBrand: { + color: 'blue', + }, + tokens: { + // Fonts + '--wa-font-family-body': 'Quicksand, sans-serif', + '--wa-font-family-heading': 'var(--wa-font-family-body)', + '--wa-font-family-code': 'ui-monospace, monospace', + '--wa-font-family-longform': '"Crimson Pro", serif', + '--wa-font-weight-body': 500, + '--wa-font-weight-heading': 700, + '--wa-font-weight-code': 500, + '--wa-font-weight-longform': 500, + + // Elements + '--wa-border-radius-scale': 1.5, + '--wa-space-scale': 1, + '--wa-border-width-scale': 2, + }, + }, + // #endregion + + // + // #region Shoelace + // + { + name: 'Shoelace', + description: 'The original, familiar look you know and love from Shoelace.', + filename: 'shoelace.css', + isPro: false, + fonts: { + body: { + name: 'OS Default', + css: 'ui-sans-serif, system-ui, sans-serif', + href: null, + }, + heading: { + name: 'OS Default', + css: 'ui-sans-serif, system-ui, sans-serif', + href: null, + }, + code: { + name: 'OS Default', + css: 'ui-monospace, monospace', + href: null, + }, + longform: { + name: 'OS Default', + css: 'ui-serif, serif', + href: null, + }, + }, + icons: { + family: 'classic', + weight: 1, + }, + palette: { + name: 'Shoelace', + filename: 'shoelace.css', + }, + colorBrand: { + color: 'blue', + }, + tokens: { + // Fonts + '--wa-font-family-body': 'ui-sans-serif, system-ui, sans-serif', + '--wa-font-family-heading': 'var(--wa-font-family-body)', + '--wa-font-family-code': 'ui-monospace, monospace', + '--wa-font-family-longform': 'ui-serif, serif', + '--wa-font-weight-body': 400, + '--wa-font-weight-heading': 600, + '--wa-font-weight-code': 400, + '--wa-font-weight-longform': 400, + + // Elements + '--wa-border-radius-scale': 0.7, + '--wa-space-scale': 1, + '--wa-border-width-scale': 1, + }, + }, + // #endregion + + // + // #region Active + // + { + name: 'Active', + description: 'Energetic and tactile, always in motion.', + filename: 'active.css', + isPro: true, + fonts: { + body: { + name: 'Inter', + css: 'Inter, sans-serif', + href: 'https://fonts.googleapis.com/css2?family=Aleo:ital,wght@0,100..900;1,100..900&family=Geist+Mono:wght@100..900&family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap', + }, + heading: { + name: 'Inter', + css: 'Inter, sans-serif', + href: 'https://fonts.googleapis.com/css2?family=Aleo:ital,wght@0,100..900;1,100..900&family=Geist+Mono:wght@100..900&family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap', + }, + code: { + name: 'Geist Mono', + css: '"Geist Mono", monospace', + href: 'https://fonts.googleapis.com/css2?family=Aleo:ital,wght@0,100..900;1,100..900&family=Geist+Mono:wght@100..900&family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap', + }, + longform: { + name: 'Aleo', + css: 'Aleo, serif', + href: 'https://fonts.googleapis.com/css2?family=Aleo:ital,wght@0,100..900;1,100..900&family=Geist+Mono:wght@100..900&family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap', + }, + }, + icons: { + family: 'classic', + weight: 1, + }, + palette: { + name: 'Rudimentary', + filename: 'rudimentary.css', + }, + colorBrand: { + color: 'green', + }, + tokens: { + // Fonts + '--wa-font-family-body': 'Inter, sans-serif', + '--wa-font-family-heading': 'var(--wa-font-family-body)', + '--wa-font-family-code': '"Geist Mono", monospace', + '--wa-font-family-longform': 'Aleo, serif', + '--wa-font-weight-body': 400, + '--wa-font-weight-heading': 650, + '--wa-font-weight-code': 400, + '--wa-font-weight-longform': 400, + + // Elements + '--wa-border-radius-scale': 1.75, + '--wa-space-scale': 1, + '--wa-border-width-scale': 1, + }, + }, + // #endregion + + // + // #region Brutalist + // + { + name: 'Brutalist', + description: 'Sharp, square, and unapologetically bold.', + filename: 'brutalist.css', + isPro: true, + fonts: { + body: { + name: 'Space Grotesk', + css: '"Space Grotesk", sans-serif', + href: 'https://fonts.googleapis.com/css2?family=IBM+Plex+Sans+Condensed:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&family=Podkova:wght@400..800&family=Space+Grotesk:wght@300..700&family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap', + }, + heading: { + name: 'IBM Plex Sans Condensed', + css: '"IBM Plex Sans Condensed", sans-serif', + href: 'https://fonts.googleapis.com/css2?family=IBM+Plex+Sans+Condensed:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&family=Podkova:wght@400..800&family=Space+Grotesk:wght@300..700&family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap', + }, + code: { + name: 'Space Mono', + css: '"Space Mono", monospace', + href: 'https://fonts.googleapis.com/css2?family=IBM+Plex+Sans+Condensed:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&family=Podkova:wght@400..800&family=Space+Grotesk:wght@300..700&family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap', + }, + longform: { + name: 'Podkova', + css: 'Podkova, serif', + href: 'https://fonts.googleapis.com/css2?family=IBM+Plex+Sans+Condensed:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&family=Podkova:wght@400..800&family=Space+Grotesk:wght@300..700&family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap', + }, + }, + icons: { + family: 'classic', + weight: 2, + }, + palette: { + name: 'Default', + filename: 'default.css', + }, + colorBrand: { + color: 'blue', + }, + tokens: { + // Fonts + '--wa-font-family-body': 'Space Grotesk, sans-serif', + '--wa-font-family-heading': 'IBM Plex Sans Condensed, sans-serif', + '--wa-font-family-code': 'Space Mono, monospace', + '--wa-font-family-longform': 'Podkova, serif', + '--wa-font-weight-body': 400, + '--wa-font-weight-heading': 500, + '--wa-font-weight-code': 400, + '--wa-font-weight-longform': 400, + + // Elements + '--wa-border-radius-scale': 0, + '--wa-space-scale': 1.125, + '--wa-border-width-scale': 2, + }, + }, + // #endregion + + // + // #region Glossy + // + { + name: 'Glossy', + description: 'Bustling with plenty of luster and shine.', + filename: 'glossy.css', + isPro: true, + fonts: { + body: { + name: 'Figtree', + css: 'Figtree, sans-serif', + href: 'https://fonts.googleapis.com/css2?family=Chivo+Mono:ital,wght@0,100..900;1,100..900&family=Figtree:ital,wght@0,300..900;1,300..900&family=Fraunces:ital,opsz,wght@0,9..144,100..900;1,9..144,100..900&display=swap', + }, + heading: { + name: 'Figtree', + css: 'Figtree, sans-serif', + href: 'https://fonts.googleapis.com/css2?family=Chivo+Mono:ital,wght@0,100..900;1,100..900&family=Figtree:ital,wght@0,300..900;1,300..900&family=Fraunces:ital,opsz,wght@0,9..144,100..900;1,9..144,100..900&display=swap', + }, + code: { + name: 'Chivo Mono', + css: '"Chivo Mono", monospace', + href: 'https://fonts.googleapis.com/css2?family=Chivo+Mono:ital,wght@0,100..900;1,100..900&family=Figtree:ital,wght@0,300..900;1,300..900&family=Fraunces:ital,opsz,wght@0,9..144,100..900;1,9..144,100..900&display=swap', + }, + longform: { + name: 'Fraunces', + css: 'Fraunces, serif', + href: 'https://fonts.googleapis.com/css2?family=Chivo+Mono:ital,wght@0,100..900;1,100..900&family=Figtree:ital,wght@0,300..900;1,300..900&family=Fraunces:ital,opsz,wght@0,9..144,100..900;1,9..144,100..900&display=swap', + }, + }, + icons: { + family: 'classic', + weight: 1, + }, + palette: { + name: 'Elegant', + filename: 'elegant.css', + }, + colorBrand: { + color: 'indigo', + }, + tokens: { + // Fonts + '--wa-font-family-body': 'Figtree, sans-serif', + '--wa-font-family-heading': 'var(--wa-font-family-body)', + '--wa-font-family-code': '"Chivo Mono", monospace', + '--wa-font-family-longform': 'Fraunces, serif', + '--wa-font-weight-body': 400, + '--wa-font-weight-heading': 800, + '--wa-font-weight-code': 400, + '--wa-font-weight-longform': 350, + + // Elements + '--wa-border-radius-scale': 1.33, + '--wa-space-scale': 1.125, + '--wa-border-width-scale': 1, + }, + }, + // #endregion + + // + // #region Matter + // + { + name: 'Matter', + description: 'Digital design inspired by the real world.', + filename: 'matter.css', + isPro: true, + fonts: { + body: { + name: 'Wix Madefor Text', + css: '"Wix Madefor Text", sans-serif', + href: 'https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&family=Roboto+Serif:ital,opsz,wght@0,8..144,100..900;1,8..144,100..900&family=Roboto:ital,wght@0,100..900;1,100..900&family=Wix+Madefor+Text:ital,wght@0,400..800;1,400..800&display=swap', + }, + heading: { + name: 'Wix Madefor Text', + css: '"Wix Madefor Text", sans-serif', + href: 'https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&family=Roboto+Serif:ital,opsz,wght@0,8..144,100..900;1,8..144,100..900&family=Roboto:ital,wght@0,100..900;1,100..900&family=Wix+Madefor+Text:ital,wght@0,400..800;1,400..800&display=swap', + }, + code: { + name: 'Roboto Mono', + css: '"Roboto Mono", monospace', + href: 'https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&family=Roboto+Serif:ital,opsz,wght@0,8..144,100..900;1,8..144,100..900&family=Roboto:ital,wght@0,100..900;1,100..900&family=Wix+Madefor+Text:ital,wght@0,400..800;1,400..800&display=swap', + }, + longform: { + name: 'Roboto Serif', + css: '"Roboto Serif", serif', + href: 'https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&family=Roboto+Serif:ital,opsz,wght@0,8..144,100..900;1,8..144,100..900&family=Roboto:ital,wght@0,100..900;1,100..900&family=Wix+Madefor+Text:ital,wght@0,400..800;1,400..800&display=swap', + }, + }, + icons: { + family: 'classic', + weight: 1, + }, + palette: { + name: 'Mild', + filename: 'mild.css', + }, + colorBrand: { + color: 'purple', + }, + tokens: { + // Fonts + '--wa-font-family-body': 'Wix Madefor Text, sans-serif', + '--wa-font-family-heading': 'var(--wa-font-family-body)', + '--wa-font-family-code': 'Roboto Mono, monospace', + '--wa-font-family-longform': 'Roboto Serif, serif', + '--wa-font-weight-body': 400, + '--wa-font-weight-heading': 500, + '--wa-font-weight-code': 400, + '--wa-font-weight-longform': 400, + + // Elements + '--wa-border-radius-scale': 1.33, + '--wa-space-scale': 1, + '--wa-border-width-scale': 1, + }, + }, + // #endregion + + // + // #region Mellow + // + { + name: 'Mellow', + description: 'Soft and soothing, like a lazy Sunday morning.', + filename: 'mellow.css', + isPro: true, + fonts: { + body: { + name: 'Mulish', + css: 'Mulish, sans-serif', + href: 'https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400..700;1,400..700&family=Mulish:ital,wght@0,200..1000;1,200..1000&display=swap', + }, + heading: { + name: 'Lora', + css: 'Lora, serif', + href: 'https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400..700;1,400..700&family=Mulish:ital,wght@0,200..1000;1,200..1000&display=swap', + }, + code: { + name: 'OS Default', + css: 'ui-monospace, monospace', + href: null, + }, + longform: { + name: 'Lora', + css: 'Lora, serif', + href: 'https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400..700;1,400..700&family=Mulish:ital,wght@0,200..1000;1,200..1000&display=swap', + }, + }, + icons: { + family: 'classic', + weight: 1.5, + }, + palette: { + name: 'Natural', + filename: 'natural.css', + }, + colorBrand: { + color: 'blue', + }, + tokens: { + // Fonts + '--wa-font-family-body': 'Mulish, sans-serif', + '--wa-font-family-heading': 'Lora, serif', + '--wa-font-family-code': 'ui-monospace, monospace', + '--wa-font-family-longform': 'Lora, serif', + '--wa-font-weight-body': 400, + '--wa-font-weight-heading': 700, + '--wa-font-weight-code': 400, + '--wa-font-weight-longform': 400, + + // Elements + '--wa-border-radius-scale': 1, + '--wa-space-scale': 1.125, + '--wa-border-width-scale': 1.5, + }, + }, + // #endregion + + // + // #region Playful + // + { + name: 'Playful', + description: 'Cheerful and engaging, like a playground on screen.', + filename: 'playful.css', + isPro: true, + fonts: { + body: { + name: 'Nunito', + css: 'Nunito, sans-serif', + href: 'https://fonts.googleapis.com/css2?family=Azeret+Mono:ital,wght@0,100..900;1,100..900&family=BioRhyme:wght@200..800&family=Fredoka:wght@300..700&family=Nunito:ital,wght@0,200..1000;1,200..1000&display=swap', + }, + heading: { + name: 'Fredoka', + css: 'Fredoka, sans-serif', + href: 'https://fonts.googleapis.com/css2?family=Azeret+Mono:ital,wght@0,100..900;1,100..900&family=BioRhyme:wght@200..800&family=Fredoka:wght@300..700&family=Nunito:ital,wght@0,200..1000;1,200..1000&display=swap', + }, + code: { + name: 'Azeret Mono', + css: '"Azeret Mono", monospace', + href: 'https://fonts.googleapis.com/css2?family=Azeret+Mono:ital,wght@0,100..900;1,100..900&family=BioRhyme:wght@200..800&family=Fredoka:wght@300..700&family=Nunito:ital,wght@0,200..1000;1,200..1000&display=swap', + }, + longform: { + name: 'BioRhyme', + css: 'BioRhyme, serif', + href: 'https://fonts.googleapis.com/css2?family=Azeret+Mono:ital,wght@0,100..900;1,100..900&family=BioRhyme:wght@200..800&family=Fredoka:wght@300..700&family=Nunito:ital,wght@0,200..1000;1,200..1000&display=swap', + }, + }, + icons: { + family: 'classic', + weight: 3, + }, + palette: { + name: 'Rudimentary', + filename: 'rudimentary.css', + }, + colorBrand: { + color: 'purple', + }, + tokens: { + // Fonts + '--wa-font-family-body': 'Nunito, sans-serif', + '--wa-font-family-heading': 'Fredoka, sans-serif', + '--wa-font-family-code': 'Azeret Mono, monospace', + '--wa-font-family-longform': 'BioRhyme, serif', + '--wa-font-weight-body': 500, + '--wa-font-weight-heading': 600, + '--wa-font-weight-code': 400, + '--wa-font-weight-longform': 400, + + // Elements + '--wa-border-radius-scale': 2, + '--wa-space-scale': 1, + '--wa-border-width-scale': 3, + }, + }, + // #endregion + + // + // #region Premium + // + { + name: 'Premium', + description: 'The ultimate in sophistication and style.', + filename: 'premium.css', + isPro: true, + fonts: { + body: { + name: 'DM Sans', + css: '"DM Sans", sans-serif', + href: 'https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&family=Playfair+Display:ital,wght@0,400..900;1,400..900&family=Playfair:ital,opsz,wght@0,5..1200,300..900;1,5..1200,300..900&display=swap', + }, + heading: { + name: 'Playfair Display', + css: '"Playfair Display", serif', + href: 'https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&family=Playfair+Display:ital,wght@0,400..900;1,400..900&family=Playfair:ital,opsz,wght@0,5..1200,300..900;1,5..1200,300..900&display=swap', + }, + code: { + name: 'OS Default', + css: 'ui-monospace, monospace', + href: null, + }, + longform: { + name: 'Playfair', + css: 'Playfair, serif', + href: 'https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&family=Playfair+Display:ital,wght@0,400..900;1,400..900&family=Playfair:ital,opsz,wght@0,5..1200,300..900;1,5..1200,300..900&display=swap', + }, + }, + icons: { + family: 'classic', + weight: 1.5, + }, + palette: { + name: 'Anodized', + filename: 'anodized.css', + }, + colorBrand: { + color: 'cyan', + }, + tokens: { + // Fonts + '--wa-font-family-body': 'DM Sans, sans-serif', + '--wa-font-family-heading': 'Playfair Display, serif', + '--wa-font-family-code': 'ui-monospace, monospace', + '--wa-font-family-longform': 'Playfair, serif', + '--wa-font-weight-body': 400, + '--wa-font-weight-heading': 500, + '--wa-font-weight-code': 400, + '--wa-font-weight-longform': 400, + + // Elements + '--wa-border-radius-scale': 0.5, + '--wa-space-scale': 1, + '--wa-border-width-scale': 1.5, + }, + }, + // #endregion + + // + // #region Tailspin + // + { + name: 'Tailspin', + description: 'Like a bird in flight, guiding you from there to here.', + filename: 'tailspin.css', + isPro: true, + fonts: { + body: { + name: 'Inter', + css: 'Inter, sans-serif', + href: 'https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap', + }, + heading: { + name: 'Inter', + css: 'Inter, sans-serif', + href: 'https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap', + }, + code: { + name: 'OS Default', + css: 'ui-monospace, monospace', + href: null, + }, + longform: { + name: 'OS Default', + css: 'ui-serif, serif', + href: null, + }, + }, + icons: { + family: 'classic', + weight: 1, + }, + palette: { + name: 'Vogue', + filename: 'vogue.css', + }, + colorBrand: { + color: 'indigo', + }, + tokens: { + // Fonts + '--wa-font-family-body': 'Inter, sans-serif', + '--wa-font-family-heading': 'var(--wa-font-family-body)', + '--wa-font-family-code': 'ui-monospace, monospace', + '--wa-font-family-longform': 'ui-serif, serif', + '--wa-font-weight-body': 400, + '--wa-font-weight-heading': 700, + '--wa-font-weight-code': 400, + '--wa-font-weight-longform': 400, + + // Elements + '--wa-border-radius-scale': 1, + '--wa-space-scale': 0.875, + '--wa-border-width-scale': 1, + }, + }, + // #endregion +]; + +/** + * All fonts used by themes, collected from the four font categories. + */ +export const fonts = themes + .flatMap(theme => [theme.fonts.body, theme.fonts.heading, theme.fonts.code, theme.fonts.longform]) + .filter( + (font, index, array) => + array.findIndex(f => f.name === font.name && f.css === font.css && f.href === font.href) === index, + ); + +/** + * Font presets derived from themes, with unique font names in order: heading > body > code > longform + */ +export const fontPresets = themes + .map(theme => { + const fontNames = [ + theme.fonts.heading.name, + theme.fonts.body.name, + theme.fonts.code.name, + theme.fonts.longform.name, + ]; + const uniqueFonts = fontNames.filter((name, index) => fontNames.indexOf(name) === index); + + return { + name: theme.name, + displayName: uniqueFonts.join(' ยท '), + fontFamilyBody: theme.fonts.body.css, + fontFamilyHeading: theme.fonts.heading.css, + fontFamilyCode: theme.fonts.code.css, + fontFamilyLongform: theme.fonts.longform.css, + fontWeightBody: theme.tokens['--wa-font-weight-body'], + fontWeightHeading: theme.tokens['--wa-font-weight-heading'], + fontWeightCode: theme.tokens['--wa-font-weight-code'], + fontWeightLongform: theme.tokens['--wa-font-weight-longform'], + }; + }) + .filter((preset, index, array) => array.findIndex(p => p.displayName === preset.displayName) === index); + +/** + * Element presets derived from themes. + */ +export const elementPresets = themes.map(theme => ({ + name: theme.name, + borderRadiusScale: theme.tokens['--wa-border-radius-scale'], + spaceScale: theme.tokens['--wa-space-scale'], + borderWidthScale: theme.tokens['--wa-border-width-scale'], +})); + +/** + * All palettes used by themes in a simple array. + */ +export const palettes = themes + .map(theme => theme.palette) + .filter( + (palette, index, array) => + array.findIndex(p => p.name === palette.name && p.filename === palette.filename) === index, + ); + +/** + * Available icons. + */ +export const icons = [ + { name: 'Classic', libraryName: 'classic' }, + { name: 'Sharp', libraryName: 'sharp' }, + { name: 'Duotone', libraryName: 'duotone' }, + { name: 'Sharp Duotone', libraryName: 'sharp-duotone' }, +]; + +export const colors = ['red', 'orange', 'yellow', 'green', 'cyan', 'blue', 'indigo', 'purple', 'pink', 'gray']; +export const tints = ['95', '90', '80', '70', '60', '50', '40', '30', '20', '10', '05']; diff --git a/packages/webawesome/docs/_data/themes.js b/packages/webawesome/docs/_data/themes.js deleted file mode 100644 index 52ea04b20..000000000 --- a/packages/webawesome/docs/_data/themes.js +++ /dev/null @@ -1,62 +0,0 @@ -import fs from 'fs'; -import path from 'path'; -// import { inlined } from '../../dist/components/icon/library.wa.js'; -const distDirectory = process.env.UNBUNDLED_DIST_DIRECTORY || path.join(path.resolve(), 'dist'); - -const THEME_DIR = path.join(distDirectory, 'styles', 'themes'); - -const themeFiles = fs.readdirSync(THEME_DIR).filter(file => file.endsWith('.css') && !file.endsWith('base.css')); - -const declarationRegex = /^\s*--wa-(?[a-z-]+)?:\s*(?.+?)\s*(\/\*.+?\*\/)?\s*;$/gm; -const importRegex = /^\s*@import\s+url\(['"](?.+?)['"]\);$/gm; -const themes = {}; - -for (const file of themeFiles) { - const id = file.replace('.css', ''); - const { imports, declarations } = readCSSFile(file); - let theme = { palette: 'default', declarations, imports }; - - for (const url of imports) { - if (url.endsWith('/color.css')) { - // Color settings - const color = readCSSFile(url); - for (const colorUrl of color.imports) { - if (colorUrl.startsWith('../../color/')) { - // Color palette - theme.palette = getFileSlug(colorUrl); - } else if (colorUrl.startsWith('../../brand/')) { - // Brand color - theme.brand = getFileSlug(colorUrl); - } - } - } else if (url.endsWith('/dimension.css')) { - theme.dimension = true; - } - } - - let icon = {}; - icon.family = theme.declarations['icon-family'] ?? theme.default?.iconFamily ?? 'classic'; - icon.variant = theme.declarations['icon-variant'] ?? theme.default?.iconVariant ?? 'solid'; - theme.icons = icon; - - theme.rounding = Number(theme.declarations['border-radius-scale'] ?? theme.default?.rounding ?? 1); - theme.spacing = Number(theme.declarations['space-scale'] ?? theme.default?.spacing ?? 1); - theme.borderWidth = Number(theme.declarations['border-width-scale'] ?? theme.default?.borderWidth ?? 1); - - themes[id] = theme; -} - -export default themes; - -function readCSSFile(url) { - const contents = fs.readFileSync(path.join(THEME_DIR, url), 'utf8'); - const imports = [...contents.matchAll(importRegex)].map(match => match.groups.path); - const declarations = Object.fromEntries( - [...contents.matchAll(declarationRegex)].map(match => [match.groups.property, match.groups.value]), - ); - return { imports, declarations }; -} - -function getFileSlug(url) { - return url.split('/').pop().replace('.css', ''); -} diff --git a/packages/webawesome/docs/_includes/base.njk b/packages/webawesome/docs/_includes/base.njk index c4421e3a2..5e8d0af79 100644 --- a/packages/webawesome/docs/_includes/base.njk +++ b/packages/webawesome/docs/_includes/base.njk @@ -8,15 +8,39 @@ + + + {% if hasSidebar %}{% endif %} - {% if hasSidebar %}{% endif %} {# Docs styles #} {% block head %}{% endblock %} + + + + @@ -40,7 +64,7 @@