diff --git a/.artifacts.yml b/.artifacts.yml index 720dbb09..263d8d15 100644 --- a/.artifacts.yml +++ b/.artifacts.yml @@ -4,6 +4,6 @@ publisher: - filter: on-branch name: publisher-branches config: - node_version: 18.x + node_version: 24.x site_build_dir: _site npm_build_script: build diff --git a/.eslintrc.json b/.eslintrc.json index a173f42d..676fb201 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -2,5 +2,23 @@ "extends": [ "@mapbox/eslint-config-mapbox", "prettier" + ], + "rules": { + "node/no-unsupported-features/es-syntax": [ + "error", + { "ignores": ["dynamicImport"] } + ] + }, + "overrides": [ + { + "files": ["*.mjs", "docs/.vitepress/**/*.js"], + "parserOptions": { + "ecmaVersion": 2022, + "sourceType": "module" + }, + "rules": { + "node/no-unsupported-features/es-syntax": "off" + } + } ] } diff --git a/.gitignore b/.gitignore index 0b9a3081..45cad3fa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ node_modules *.log dist -_tmp_assembly -_batfish* _site +docs/.vitepress/cache .DS_Store \ No newline at end of file diff --git a/.stylelintrc b/.stylelintrc deleted file mode 100644 index 0c144434..00000000 --- a/.stylelintrc +++ /dev/null @@ -1,109 +0,0 @@ -{ - "rules": { - "at-rule-empty-line-before": ["always", { - "except": [ "blockless-after-same-name-blockless"], - "ignore": ["after-comment"] - }], - "at-rule-name-case": "lower", - "at-rule-name-space-after": "always-single-line", - "at-rule-no-vendor-prefix": true, - "at-rule-semicolon-newline-after": "always", - "block-closing-brace-newline-after": "always", - "block-closing-brace-newline-before": "always-multi-line", - "block-closing-brace-space-before": "always-single-line", - "block-no-empty": true, - "block-opening-brace-newline-after": "always-multi-line", - "block-opening-brace-space-after": "always-single-line", - "block-opening-brace-space-before": "always", - "color-hex-case": "lower", - "color-named": "never", - "color-no-invalid-hex": true, - "comment-no-empty": true, - "comment-whitespace-inside": "always", - "declaration-bang-space-after": "never", - "declaration-bang-space-before": "always", - "declaration-block-no-duplicate-properties": [ true, { - "ignore": ["consecutive-duplicates-with-different-values"] - }], - "declaration-block-no-shorthand-property-overrides": true, - "declaration-block-semicolon-newline-after": "always-multi-line", - "declaration-block-semicolon-space-after": "always-single-line", - "declaration-block-semicolon-space-before": "never", - "declaration-block-single-line-max-declarations": 2, - "declaration-block-trailing-semicolon": "always", - "declaration-colon-space-after": "always", - "declaration-colon-space-before": "never", - "declaration-empty-line-before": ["always", { - "except": [ - "after-declaration", - "first-nested" - ], - "ignore": [ - "after-comment", - "inside-single-line-block" - ] - }], - "font-family-name-quotes": "always-unless-keyword", - "font-family-no-duplicate-names": true, - "function-calc-no-unspaced-operator": true, - "function-comma-newline-after": "always-multi-line", - "function-comma-space-after": "always-single-line", - "function-comma-space-before": "never", - "function-linear-gradient-no-nonstandard-direction": true, - "function-max-empty-lines": 0, - "function-name-case": "lower", - "function-parentheses-newline-inside": "always-multi-line", - "function-parentheses-space-inside": "never-single-line", - "function-url-quotes": "always", - "function-whitespace-after": "always", - "indentation": 2, - "keyframe-declaration-no-important": true, - "length-zero-no-unit": true, - "max-empty-lines": 1, - "media-feature-colon-space-after": "always", - "media-feature-colon-space-before": "never", - "media-feature-name-case": "lower", - "media-feature-name-no-unknown": true, - "media-feature-parentheses-space-inside": "never", - "media-query-list-comma-newline-after": "always-multi-line", - "media-query-list-comma-space-after": "always-single-line", - "media-query-list-comma-space-before": "never", - "no-empty-source": true, - "no-eol-whitespace": true, - "no-extra-semicolons": true, - "no-invalid-double-slash-comments": true, - "no-missing-end-of-source-newline": true, - "number-leading-zero": "always", - "number-no-trailing-zeros": true, - "property-case": "lower", - "property-no-unknown": true, - "property-no-vendor-prefix": true, - "selector-class-pattern": "^[a-z]+[0-9]*(-{1,2}(?!small|medium|large|md|lg|top|bottom|left|right)[\\\\/a-z0-9]+?)*$", - "selector-combinator-space-after": "always", - "selector-combinator-space-before": "always", - "selector-descendant-combinator-no-non-space": true, - "selector-list-comma-newline-after": "always", - "selector-list-comma-space-before": "never", - "selector-max-empty-lines": 0, - "selector-max-id": 0, - "selector-no-vendor-prefix": true, - "selector-pseudo-class-case": "lower", - "selector-pseudo-class-no-unknown": true, - "selector-pseudo-class-parentheses-space-inside": "never", - "selector-pseudo-element-case": "lower", - "selector-pseudo-element-colon-notation": "double", - "selector-pseudo-element-no-unknown": true, - "selector-type-case": "lower", - "selector-type-no-unknown": true, - "shorthand-property-no-redundant-values": true, - "string-no-newline": true, - "string-quotes": "single", - "unit-case": "lower", - "unit-no-unknown": true, - "value-list-comma-newline-after": "always-multi-line", - "value-list-comma-space-after": "always-single-line", - "value-list-comma-space-before": "never", - "value-list-max-empty-lines": 0, - "value-no-vendor-prefix": true - } -} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 1cd6e2a3..00000000 --- a/.travis.yml +++ /dev/null @@ -1,7 +0,0 @@ -dist: jammy -language: node_js -node_js: - - 18 -cache: - directories: - - $HOME/.npm diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a7e0ea0..f347ba1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 2.0.0 + +- [breaking] Rebuild Assembly's CSS pipeline on [UnoCSS](https://unocss.dev/). Class names and `buildUserAssets` stay compatible; the generated stylesheet's formatting, comments, and source maps may differ. Node.js 24+ is required. +- [add] Export `presetAssembly()` for on-demand UnoCSS builds that keep Assembly class names. Pass `{ safelist: true }` only when you want the full prebuilt utility set. +- [add] `examples/jit` — a sample app that generates only the utilities used in its markup (no reset or component preflight). +- [internal] Assembly utilities are UnoCSS rules in `src/preset/`. Reset and component CSS live in `src/preset/base.css`. The package build is `src/build-*.js`. The documentation site is VitePress under `docs/`. + ## 1.16.0 - [add] `ai` icon diff --git a/MIGRATION_GUIDES.md b/MIGRATION_GUIDES.md index 2bf11555..48403458 100644 --- a/MIGRATION_GUIDES.md +++ b/MIGRATION_GUIDES.md @@ -1,5 +1,32 @@ # Migration Guides +## 2.0.0 + +Version 2.0.0 rebuilds Assembly with [UnoCSS](https://unocss.dev/) while keeping the existing class names and `buildUserAssets` API. + +You do not need to change class names in markup. Continue to use `assembly.css` / `assembly.js`, or keep calling `Assembly.buildUserAssets`. + +What can change: + +- The contents of `assembly.css` are generated by UnoCSS, so whitespace, comments, rule order, and source maps may differ from 1.x. Selectors and declarations for public classes stay the same. +- Layout-scale media variants (`px12-mm`, `w-1/2-ml`, and so on) now use the same custom media queries you pass to `buildUserAssets`, instead of the hardcoded default breakpoints. +- `@mapbox/assembly` now depends on `@unocss/core` (used when creating custom builds) and requires Node.js 24+. + +New: you can use Assembly as an UnoCSS preset for on-demand CSS: + +```js +import { defineConfig } from 'unocss'; +import { presetAssembly } from '@mapbox/assembly'; + +export default defineConfig({ + presets: [presetAssembly()] +}); +``` + +`presetAssembly()` tree-shakes utilities from your source. The prebuilt `assembly.css` uses `presetAssembly({ safelist: true })`. Reset, forms, buttons, and other component CSS are preflight and always included. + +The generated CSS still uses Assembly's CSS variables and custom media queries, so it needs a PostCSS step with `postcss-custom-properties` and `postcss-custom-media`. See [`presetAssembly` in the README](README.md#presetassemblyoptions) for the config. + ## 1.0.0+ Version 1.0.0 of Assembly introduces breaking changes – learn how to resolve them with this handy guide. diff --git a/README.md b/README.md index 698f9c3e..457244af 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,45 @@ Assembly exposes a JS module for creating custom builds. Why might you want to c - You want to append extra stylesheets that also use Assembly's variables and media queries. - You want to reduce file-size by picking and choosing the color variants you need. +### presetAssembly([options]) + +Returns an [UnoCSS preset](https://unocss.dev/guide/presets) that generates Assembly class names on demand. Utilities, media variants, and color modifiers are tree-shaken from your source. Reset, form, and other component CSS is included as preflight. + +```js +import { defineConfig } from 'unocss'; +import { presetAssembly } from '@mapbox/assembly'; + +export default defineConfig({ + presets: [presetAssembly()] +}); +``` + +The generated CSS uses Assembly's CSS variables (`var(--blue)`) and custom media queries (`@media (--m-screen)`), which browsers can't resolve on their own. Run it through PostCSS with the same plugins `buildUserAssets` uses, pointed at Assembly's variable and media query definitions: + +```js +// postcss.config.js +const variables = require('@mapbox/assembly/src/variables.json'); +const mediaQueries = require('@mapbox/assembly/src/media-queries.json'); + +module.exports = { + plugins: [ + require('postcss-custom-properties')({ + preserve: false, + importFrom: { customProperties: variables } + }), + require('postcss-custom-media')({ + importFrom: { customMedia: mediaQueries } + }) + ] +}; +``` + +`importFrom` requires `postcss-custom-properties@12` and `postcss-custom-media@8`; later major versions removed it. To change a variable or breakpoint, override its entry in these objects. `examples/jit/build.mjs` shows the full pipeline. + +The prebuilt `assembly.css` is generated with `presetAssembly({ safelist: true })` so every class is present. Do not pass `safelist: true` in an app that wants on-demand CSS. + +`options.colorVariants` and `options.files` match the `buildUserAssets` options of the same names. Variable overrides are applied in the PostCSS step above, not by the preset. + ### buildUserAssets(outdir[, options]) Returns a Promise that resolves when all assets have been written to `outdir`. @@ -125,30 +164,27 @@ Assembly strives for flat, single rule declarations and avoids overrides wheneve ### Media query class variants -Media query class variants (e.g. `block-mm` as a variant of `block`) are automatically generated and added to the CSS build with `scripts/build-media-variants.js`. If you want to generate media variants for a new class, or change which classes get media variants, you'll need to modify the lists in that file. +Media query class variants (e.g. `block-mm` as a variant of `block`) are generated by the Assembly UnoCSS preset (`src/preset/variants.js`). The prebuilt stylesheet includes `-mm`, `-ml`, and `-mxl` variants for the classes listed in `src/preset/media-classes.js` plus every layout-scale utility. If you use `presetAssembly()` on demand, those suffixes work for any matching utility. ## Development ### Tools +- [UnoCSS](https://unocss.dev/) generates Assembly utilities from `presetAssembly()` (`src/preset/utilities.js`, layout scales, color rules, media-query variants, and `src/preset/base.css` for reset/forms). - [PostCSS](http://postcss.org/) for processing CSS. PostCSS parses CSS and runs it through plugins, and these are the plugins we're using: - [Autoprefixer](https://autoprefixer.github.io/) automatically adds vendor prefixes. - [postcss-custom-properties](https://github.com/postcss/postcss-custom-properties) allows us to use variables for values, with the [CSS custom properties syntax](https://developer.mozilla.org/en-US/docs/Web/CSS/--*). - [postcss-custom-media](https://github.com/postcss/postcss-custom-media) allows us to use variables for media queries, with the [CSS custom media queries syntax](https://www.w3.org/TR/2016/WD-mediaqueries-4-20160126/#custom-mq). - - [concat-with-sourcemaps](https://github.com/floridoo/concat-with-sourcemaps) allows us to split our CSS into multiple stylesheets. -- [stylelint](http://stylelint.io/) lints our CSS. - SVGs - [svgstore](https://github.com/svgstore/svgstore) compiles our SVGs into a SVG "sprite" of sorts, allowing us to use [the latest and greatest SVG-based icon system](https://css-tricks.com/svg-sprites-use-better-icon-fonts/). -- Documentation - - [documentation-css](https://github.com/documentationjs/documentation-css) parses annotation comments in the CSS, outputting objects that can be used to build documentation. -- [Batfish](https://github.com/mapbox/batfish) powers the website. +- [VitePress](https://vitepress.dev/) powers the website. ### Install and start ```bash npm ci # Installs your `node_modules` -npm start # Builds everything, starts a dev server, rebuilds & reloads on changes +npm run dev # Builds CSS/JS, starts the VitePress docs server npm run build:js # Build SVGs and other JS ``` diff --git a/babel.config.json b/babel.config.json deleted file mode 100644 index 0960c012..00000000 --- a/babel.config.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "presets": [ - "@babel/preset-env", - "@babel/preset-react" - ] -} \ No newline at end of file diff --git a/batfish.config.js b/batfish.config.js deleted file mode 100644 index 33b0ae9b..00000000 --- a/batfish.config.js +++ /dev/null @@ -1,40 +0,0 @@ -'use strict'; - -require('./src/md4-shim'); - -const path = require('path'); -// eslint-disable-next-line -const navigationStructure = require('./_tmp_assembly/navigation.json'); - -module.exports = () => { - return { - siteBasePath: '/assembly/', - siteOrigin: 'https://labs.mapbox.com', - outputDirectory: path.join(__dirname, '_site'), - stylesheets: [ - path.join(__dirname, './site/css/hljs.css'), - path.join(__dirname, './dist/assembly.css') - ], - dataSelectors: { - examplesSubNavigationList: () => { - return navigationStructure.navigationList.find( - routeConfig => routeConfig.name === 'Examples' - ).items; - }, - documentationData: () => { - return navigationStructure.navigationList.find( - routeConfig => routeConfig.name === 'Documentation' - ).props.documentationData; - } - }, - fileLoaderExtensions: extensions => extensions.concat(['svg']), - pagesDirectory: path.join(__dirname, './site/pages'), - webpackStaticIgnore: [/copy\.js$/], - vendorModules: ['highlight.js'], - applicationWrapperPath: path.join( - __dirname, - './site/application_wrapper.js' - ), - staticHtmlInlineDeferCss: false - }; -}; diff --git a/docs/.vitepress/config.mjs b/docs/.vitepress/config.mjs new file mode 100644 index 00000000..58774ae0 --- /dev/null +++ b/docs/.vitepress/config.mjs @@ -0,0 +1,67 @@ +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import { defineConfig } from 'vitepress'; + +import { documentationLayout } from './documentation-layout.js'; +import { htmlExampleMarkup } from './html-example-markup.js'; +import hljsGithub from './hljs-github-theme.js'; + +const configDir = path.dirname(fileURLToPath(import.meta.url)); +const pkg = JSON.parse( + fs.readFileSync(path.resolve(configDir, '../../package.json'), 'utf8') +); +const base = '/assembly/'; + +function packageVersion(md) { + md.core.ruler.push('package_version', state => { + state.tokens.forEach(token => { + if (token.type === 'fence' || token.type === 'html_block') { + token.content = token.content.replaceAll('%VERSION%', pkg.version); + } + }); + }); +} + +function exampleFence(md) { + const defaultFence = md.renderer.rules.fence; + md.renderer.rules.fence = (tokens, idx, options, env, slf) => { + const token = tokens[idx]; + if (token.info.trim() === 'example') { + return `${htmlExampleMarkup(options.highlight, token.content)}\n`; + } + return `
${defaultFence(tokens, idx, options, env, slf)}
\n`; + }; +} + +export default defineConfig({ + title: 'Assembly.css', + description: + 'A CSS framework that makes the hard parts of building anything on the web easy.', + base, + head: [ + ['link', { rel: 'icon', type: 'image/x-icon', href: `${base}favicon.ico` }] + ], + outDir: '../_site', + srcExclude: ['documentation/!(index).md'], + cleanUrls: true, + appearance: false, + vite: { + server: { + fs: { + allow: ['..'] + } + } + }, + markdown: { + theme: hljsGithub, + anchor: { + permalink: false + }, + config(md) { + packageVersion(md); + exampleFence(md); + documentationLayout(md); + } + } +}); diff --git a/docs/.vitepress/documentation-layout.js b/docs/.vitepress/documentation-layout.js new file mode 100644 index 00000000..82253f54 --- /dev/null +++ b/docs/.vitepress/documentation-layout.js @@ -0,0 +1,187 @@ +import { createRequire } from 'module'; + +const require = createRequire(import.meta.url); +const { ALL_COLORS } = require('../../src/preset/color-utils.js'); +const { presetAssembly } = require('../../src/preset.js'); +const layoutScales = require('../../src/scales.json'); + +const SAFELIST = new Set(presetAssembly({ safelist: true }).safelist); + +const SELECTOR_VALUES = Object.assign( + { color: ALL_COLORS.concat('text') }, + layoutScales +); + +const COLOR_GRIDS = { + color: { gridClass: 'txt-s grid', itemClass: '' }, + bg: { gridClass: 'grid', itemClass: ' py6 px6' } +}; + +// A `{key}` placeholder expands to every value of `SELECTOR_VALUES[key]` whose +// class Assembly generates. Lines are interleaved per value. +function expandSelectors(lines) { + const placeholder = lines.join('\n').match(/\{(\w+)\}/); + if (!placeholder) return lines; + return SELECTOR_VALUES[placeholder[1]] + .flatMap(value => + lines.map(line => line.replace(placeholder[0], String(value))) + ) + .filter(selector => SAFELIST.has(selector.slice(1))); +} + +function isFence(token, info) { + return token.type === 'fence' && token.info.trim() === info; +} + +function isEntryBoundary(token) { + return token.type === 'heading_open' || isFence(token, 'selectors'); +} + +function nextBoundary(tokens, fromIndex) { + const index = tokens.findIndex( + (token, i) => i >= fromIndex && isEntryBoundary(token) + ); + return index === -1 ? tokens.length : index; +} + +function htmlToken(state, content) { + const token = new state.Token('html_block', '', 0); + token.content = content; + return token; +} + +function expandColorGrids(state) { + state.tokens.forEach(token => { + const [kind, prefix] = token.info.trim().split(/\s+/); + if (token.type !== 'fence' || kind !== 'color-grid') return; + const { gridClass, itemClass } = COLOR_GRIDS[prefix]; + const items = expandSelectors([`.${prefix}-{color}`]).map(selector => { + const name = selector.slice(1); + return `
${name}
`; + }); + token.info = 'example'; + token.content = `
\n${items.join('\n')}\n
\n`; + }); +} + +function escapeHtml(value) { + return value + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(/ 1 + ? 'mr3 py3 color-blue-deep round bg-blue-faint mb3 inline-block txt-s px3' + : 'mr3 py3 color-blue-deep round bg-blue-faint mb3 inline-block px6'; + return selectors + .map(selector => { + const id = selector.replace(/\s+/g, '-').replace(/\./g, ''); + const idAttr = usedIds.has(id) ? '' : ` id="${escapeHtml(id)}"`; + usedIds.add(id); + return `${escapeHtml( + selector + )}`; + }) + .join(''); +} + +function wrapEntry(state, openIndex, md, usedIds) { + const tokens = state.tokens; + const end = nextBoundary(tokens, openIndex + 1); + const selectors = expandSelectors( + tokens[openIndex].content.trim().split('\n') + ); + const inner = tokens.slice(openIndex + 1, end); + const exampleAt = inner.findIndex(token => isFence(token, 'example')); + const split = exampleAt === -1 ? inner.length : exampleAt; + const render = list => md.renderer.render(list, md.options, state.env); + tokens.splice( + openIndex, + end - openIndex, + htmlToken( + state, + `
+
+
+
${pillsHtml( + selectors, + usedIds + )}
+
+
+
${render(inner.slice(0, split))}
+${render(inner.slice(split))} +
+
+
\n` + ) + ); +} + +function wrapIntro(state, openIndex) { + const tokens = state.tokens; + const closeIndex = tokens.findIndex( + (token, index) => index > openIndex && token.type === 'heading_close' + ); + const end = nextBoundary(tokens, closeIndex + 1); + const inner = tokens.slice(closeIndex + 1, end); + if (inner.length === 0) return; + tokens.splice( + closeIndex + 1, + inner.length, + htmlToken(state, '
\n'), + ...inner, + htmlToken(state, '
\n') + ); +} + +function headingOpens(tokens) { + return tokens + .map((token, index) => (token.type === 'heading_open' ? index : -1)) + .filter(index => index >= 0); +} + +const HEADING_CLASSES = { + h1: 'txt-h2 txt-bold mb18 pt24', + h2: 'txt-l txt-bold pt12 mt12' +}; + +function linkToSelf(state, heading, inline) { + const open = new state.Token('link_open', 'a', 1); + open.attrs = [['class', 'block'], ['href', `#${heading.attrGet('id')}`]]; + inline.children = [ + open, + ...inline.children, + new state.Token('link_close', 'a', -1) + ]; +} + +function styleHeadings(state) { + state.tokens.forEach((token, index) => { + if (token.type !== 'heading_open') return; + token.attrJoin('class', HEADING_CLASSES[token.tag]); + linkToSelf(state, token, state.tokens[index + 1]); + }); +} + +export function documentationLayout(md) { + md.core.ruler.push('documentation_layout', state => { + if (!state.env.relativePath?.startsWith('documentation/')) return; + expandColorGrids(state); + styleHeadings(state); + headingOpens(state.tokens) + .reverse() + .forEach(index => wrapIntro(state, index)); + const usedIds = new Set( + headingOpens(state.tokens).map(index => state.tokens[index].attrGet('id')) + ); + for (let index = 0; index < state.tokens.length; index++) { + if (isFence(state.tokens[index], 'selectors')) { + wrapEntry(state, index, md, usedIds); + } + } + }); +} diff --git a/docs/.vitepress/hljs-github-theme.js b/docs/.vitepress/hljs-github-theme.js new file mode 100644 index 00000000..c97661d2 --- /dev/null +++ b/docs/.vitepress/hljs-github-theme.js @@ -0,0 +1,57 @@ +/** highlight.js GitHub theme, as a Shiki theme for VitePress. */ +export default { + name: 'hljs-github', + type: 'light', + colors: { + 'editor.background': '#ffffff', + 'editor.foreground': '#333333' + }, + settings: [ + { + scope: ['comment', 'punctuation.definition.comment', 'string.comment'], + settings: { foreground: '#999988', fontStyle: 'italic' } + }, + { + scope: ['keyword', 'storage', 'storage.type', 'storage.modifier'], + settings: { foreground: '#333333', fontStyle: 'bold' } + }, + { + scope: [ + 'constant.numeric', + 'constant.language', + 'variable', + 'variable.other' + ], + settings: { foreground: '#008080' } + }, + { + scope: [ + 'string', + 'string.quoted', + 'punctuation.definition.string', + 'string punctuation.section.embedded source' + ], + settings: { foreground: '#DD1144' } + }, + { + scope: ['entity.name.tag', 'punctuation.definition.tag'], + settings: { foreground: '#000080' } + }, + { + scope: ['entity', 'entity.name'], + settings: { foreground: '#000080' } + }, + { + scope: ['entity.other.attribute-name'], + settings: { foreground: '#008080' } + }, + { + scope: ['support', 'support.constant', 'support.variable'], + settings: { foreground: '#0086B3' } + }, + { + scope: ['constant', 'entity.name.constant'], + settings: { foreground: '#008080' } + } + ] +}; diff --git a/docs/.vitepress/html-example-markup.js b/docs/.vitepress/html-example-markup.js new file mode 100644 index 00000000..c3e59f4f --- /dev/null +++ b/docs/.vitepress/html-example-markup.js @@ -0,0 +1,7 @@ +export function htmlExampleMarkup(highlight, content) { + const source = content.replace(/\n$/, ''); + const highlighted = highlight(source, 'html', ''); + return ``; +} diff --git a/docs/.vitepress/theme/Catalog.vue b/docs/.vitepress/theme/Catalog.vue new file mode 100644 index 00000000..8c7cbaf8 --- /dev/null +++ b/docs/.vitepress/theme/Catalog.vue @@ -0,0 +1,83 @@ + + + diff --git a/docs/.vitepress/theme/ExamplesIndex.vue b/docs/.vitepress/theme/ExamplesIndex.vue new file mode 100644 index 00000000..359e7aaf --- /dev/null +++ b/docs/.vitepress/theme/ExamplesIndex.vue @@ -0,0 +1,21 @@ + + + diff --git a/docs/.vitepress/theme/HtmlExample.vue b/docs/.vitepress/theme/HtmlExample.vue new file mode 100644 index 00000000..1e887264 --- /dev/null +++ b/docs/.vitepress/theme/HtmlExample.vue @@ -0,0 +1,39 @@ + + + diff --git a/docs/.vitepress/theme/IconGrid.vue b/docs/.vitepress/theme/IconGrid.vue new file mode 100644 index 00000000..8a6a9115 --- /dev/null +++ b/docs/.vitepress/theme/IconGrid.vue @@ -0,0 +1,18 @@ + + + diff --git a/docs/.vitepress/theme/Layout.vue b/docs/.vitepress/theme/Layout.vue new file mode 100644 index 00000000..8cee1cbc --- /dev/null +++ b/docs/.vitepress/theme/Layout.vue @@ -0,0 +1,183 @@ + + + diff --git a/docs/.vitepress/theme/LayoutScales.vue b/docs/.vitepress/theme/LayoutScales.vue new file mode 100644 index 00000000..1a9ae282 --- /dev/null +++ b/docs/.vitepress/theme/LayoutScales.vue @@ -0,0 +1,87 @@ + + + diff --git a/docs/.vitepress/theme/Logo.vue b/docs/.vitepress/theme/Logo.vue new file mode 100644 index 00000000..01e180db --- /dev/null +++ b/docs/.vitepress/theme/Logo.vue @@ -0,0 +1,9 @@ + diff --git a/docs/.vitepress/theme/catalog/Bleeds.vue b/docs/.vitepress/theme/catalog/Bleeds.vue new file mode 100644 index 00000000..ce1203f5 --- /dev/null +++ b/docs/.vitepress/theme/catalog/Bleeds.vue @@ -0,0 +1,27 @@ + diff --git a/docs/.vitepress/theme/catalog/Buttons.vue b/docs/.vitepress/theme/catalog/Buttons.vue new file mode 100644 index 00000000..63e83631 --- /dev/null +++ b/docs/.vitepress/theme/catalog/Buttons.vue @@ -0,0 +1,76 @@ + + + diff --git a/docs/.vitepress/theme/catalog/Checkboxes.vue b/docs/.vitepress/theme/catalog/Checkboxes.vue new file mode 100644 index 00000000..af118ea2 --- /dev/null +++ b/docs/.vitepress/theme/catalog/Checkboxes.vue @@ -0,0 +1,37 @@ + + + diff --git a/docs/.vitepress/theme/catalog/Flexbox.vue b/docs/.vitepress/theme/catalog/Flexbox.vue new file mode 100644 index 00000000..ee14593b --- /dev/null +++ b/docs/.vitepress/theme/catalog/Flexbox.vue @@ -0,0 +1,111 @@ + diff --git a/docs/.vitepress/theme/catalog/Grids.vue b/docs/.vitepress/theme/catalog/Grids.vue new file mode 100644 index 00000000..fcc2ac7f --- /dev/null +++ b/docs/.vitepress/theme/catalog/Grids.vue @@ -0,0 +1,93 @@ + + + diff --git a/docs/.vitepress/theme/catalog/Inputs.vue b/docs/.vitepress/theme/catalog/Inputs.vue new file mode 100644 index 00000000..4b943c90 --- /dev/null +++ b/docs/.vitepress/theme/catalog/Inputs.vue @@ -0,0 +1,65 @@ + + + diff --git a/docs/.vitepress/theme/catalog/Links.vue b/docs/.vitepress/theme/catalog/Links.vue new file mode 100644 index 00000000..e90cd2b6 --- /dev/null +++ b/docs/.vitepress/theme/catalog/Links.vue @@ -0,0 +1,47 @@ + + + diff --git a/docs/.vitepress/theme/catalog/Lists.vue b/docs/.vitepress/theme/catalog/Lists.vue new file mode 100644 index 00000000..7c973e4e --- /dev/null +++ b/docs/.vitepress/theme/catalog/Lists.vue @@ -0,0 +1,157 @@ + diff --git a/docs/.vitepress/theme/catalog/Pills.vue b/docs/.vitepress/theme/catalog/Pills.vue new file mode 100644 index 00000000..c3c7107a --- /dev/null +++ b/docs/.vitepress/theme/catalog/Pills.vue @@ -0,0 +1,47 @@ + diff --git a/docs/.vitepress/theme/catalog/Radios.vue b/docs/.vitepress/theme/catalog/Radios.vue new file mode 100644 index 00000000..7d63cb19 --- /dev/null +++ b/docs/.vitepress/theme/catalog/Radios.vue @@ -0,0 +1,29 @@ + + + diff --git a/docs/.vitepress/theme/catalog/Ranges.vue b/docs/.vitepress/theme/catalog/Ranges.vue new file mode 100644 index 00000000..49f38c16 --- /dev/null +++ b/docs/.vitepress/theme/catalog/Ranges.vue @@ -0,0 +1,55 @@ + + + diff --git a/docs/.vitepress/theme/catalog/Selects.vue b/docs/.vitepress/theme/catalog/Selects.vue new file mode 100644 index 00000000..dc9ceb3f --- /dev/null +++ b/docs/.vitepress/theme/catalog/Selects.vue @@ -0,0 +1,79 @@ + + + diff --git a/docs/.vitepress/theme/catalog/Switches.vue b/docs/.vitepress/theme/catalog/Switches.vue new file mode 100644 index 00000000..93cbc017 --- /dev/null +++ b/docs/.vitepress/theme/catalog/Switches.vue @@ -0,0 +1,41 @@ + + + diff --git a/docs/.vitepress/theme/catalog/Tables.vue b/docs/.vitepress/theme/catalog/Tables.vue new file mode 100644 index 00000000..dd5e3ff8 --- /dev/null +++ b/docs/.vitepress/theme/catalog/Tables.vue @@ -0,0 +1,190 @@ + diff --git a/docs/.vitepress/theme/catalog/Textareas.vue b/docs/.vitepress/theme/catalog/Textareas.vue new file mode 100644 index 00000000..b32d8d09 --- /dev/null +++ b/docs/.vitepress/theme/catalog/Textareas.vue @@ -0,0 +1,36 @@ + + + diff --git a/docs/.vitepress/theme/catalog/Toggles.vue b/docs/.vitepress/theme/catalog/Toggles.vue new file mode 100644 index 00000000..61ad2038 --- /dev/null +++ b/docs/.vitepress/theme/catalog/Toggles.vue @@ -0,0 +1,119 @@ + + + diff --git a/docs/.vitepress/theme/catalog/Triangles.vue b/docs/.vitepress/theme/catalog/Triangles.vue new file mode 100644 index 00000000..efb0b3a4 --- /dev/null +++ b/docs/.vitepress/theme/catalog/Triangles.vue @@ -0,0 +1,34 @@ + + + diff --git a/docs/.vitepress/theme/catalog/Typography.vue b/docs/.vitepress/theme/catalog/Typography.vue new file mode 100644 index 00000000..6c6c255e --- /dev/null +++ b/docs/.vitepress/theme/catalog/Typography.vue @@ -0,0 +1,119 @@ + + + diff --git a/docs/.vitepress/theme/colors.data.js b/docs/.vitepress/theme/colors.data.js new file mode 100644 index 00000000..1da847ab --- /dev/null +++ b/docs/.vitepress/theme/colors.data.js @@ -0,0 +1,18 @@ +import { createRequire } from 'module'; + +const require = createRequire(import.meta.url); +const { + ALL_COLORS, + isNotAccessibleForButtons, + isNotAccessibleForForms +} = require('../../../src/preset/color-utils.js'); + +export default { + load() { + return { + all: ALL_COLORS, + button: ALL_COLORS.filter(color => !isNotAccessibleForButtons(color)), + form: ALL_COLORS.filter(color => !isNotAccessibleForForms(color)) + }; + } +}; diff --git a/docs/.vitepress/theme/data.js b/docs/.vitepress/theme/data.js new file mode 100644 index 00000000..3d833cbd --- /dev/null +++ b/docs/.vitepress/theme/data.js @@ -0,0 +1,15 @@ +import { data as colors } from './colors.data.js'; +import scales from '../../../src/scales.json'; + +function scaleToken(value) { + if (Array.isArray(value)) { + return String(value[0]).replace(/\\/g, ''); + } + return String(value).replace(/\\/g, ''); +} + +const ALL_COLORS = colors.all; +const buttonColors = colors.button; +const formColors = colors.form; + +export { ALL_COLORS, scales, scaleToken, buttonColors, formColors }; diff --git a/docs/.vitepress/theme/docs.css b/docs/.vitepress/theme/docs.css new file mode 100644 index 00000000..6d2235cd --- /dev/null +++ b/docs/.vitepress/theme/docs.css @@ -0,0 +1,21 @@ +div[class^='language-'] > button.copy, +div[class^='language-'] > span.lang { + display: none; +} + +.pre [class^='language-'] { + background: transparent; + padding: 0; + margin: 0; +} + +.pre .shiki, +.pre .shiki code { + background: transparent; + padding: 0; + margin: 0; + overflow: visible; + font-size: inherit; + line-height: inherit; + white-space: inherit; +} diff --git a/docs/.vitepress/theme/icons.data.js b/docs/.vitepress/theme/icons.data.js new file mode 100644 index 00000000..da683875 --- /dev/null +++ b/docs/.vitepress/theme/icons.data.js @@ -0,0 +1,8 @@ +import path from 'path'; + +export default { + watch: ['../../../src/svgs/*.svg'], + load(files) { + return files.map(file => path.basename(file, '.svg')).sort(); + } +}; diff --git a/docs/.vitepress/theme/index.js b/docs/.vitepress/theme/index.js new file mode 100644 index 00000000..8fb3c729 --- /dev/null +++ b/docs/.vitepress/theme/index.js @@ -0,0 +1,11 @@ +import Layout from './Layout.vue'; +import HtmlExample from './HtmlExample.vue'; +import '../../../dist/assembly.css'; +import './docs.css'; + +export default { + Layout, + enhanceApp({ app }) { + app.component('HtmlExample', HtmlExample); + } +}; diff --git a/docs/.vitepress/theme/site-nav.js b/docs/.vitepress/theme/site-nav.js new file mode 100644 index 00000000..5fa8504c --- /dev/null +++ b/docs/.vitepress/theme/site-nav.js @@ -0,0 +1,120 @@ +const exampleLinks = [ + { name: 'Badges', href: '/examples/badges' }, + { name: 'Inputs', href: '/examples/inputs' }, + { name: 'Legends', href: '/examples/legends' }, + { name: 'Modal', href: '/examples/modal' }, + { name: 'Navigation', href: '/examples/navigation' }, + { name: 'Progress bars', href: '/examples/progress-bars' }, + { name: 'Sidebar apps', href: '/examples/sidebar-apps' }, + { name: 'Equal-height columns', href: '/examples/equal-height-columns' }, + { name: 'Tooltips', href: '/examples/tooltips' }, + { name: 'Spinner', href: '/examples/spinner' } +]; + +const catalogLinks = [ + { name: 'Typography', id: 'Typography' }, + { name: 'Grids', id: 'Grids' }, + { name: 'Flexbox', id: 'Flexbox' }, + { name: 'Bleeds', id: 'Bleeds' }, + { name: 'Tables', id: 'Tables' }, + { name: 'Lists', id: 'Lists' }, + { name: 'Links', id: 'Links' }, + { name: 'Buttons', id: 'Buttons' }, + { name: 'Pills', id: 'Pills' }, + { name: 'Inputs', id: 'Inputs' }, + { name: 'Textareas', id: 'Textareas' }, + { name: 'Selects', id: 'Selects' }, + { name: 'Ranges', id: 'Ranges' }, + { name: 'Toggles', id: 'Toggles' }, + { name: 'Switches', id: 'Switches' }, + { name: 'Checkboxes', id: 'Checkboxes' }, + { name: 'Radios', id: 'Radios' }, + { name: 'Triangles', id: 'Triangles' } +]; + +function docLink(name) { + return { + name, + href: `/documentation/#${name + .toLowerCase() + .replace(/&/g, '') + .replace(/\s+/g, '-') + .replace(/-+/g, '-')}` + }; +} + +function docSection(name, headings = []) { + return Object.assign(docLink(name), { items: headings.map(docLink) }); +} + +const nav = [ + { + name: 'Home', + href: '/', + items: [ + { name: 'Getting started', href: '/#getting-started' }, + { name: 'Philosophy', href: '/#philosophy' }, + { name: 'Overview', href: '/#overview' }, + { name: 'Javascript API', href: '/#javascript-api' } + ] + }, + { + name: 'Documentation', + href: '/documentation/', + isDoc: true, + items: [ + docSection('Typography', ['Type basics', 'Type utils', 'Prose']), + docSection('Layout', [ + 'Grid', + 'Display', + 'Positioning', + 'Margins', + 'Padding', + 'Sizing', + 'Flexbox', + 'Layout utils' + ]), + docSection('Theming', [ + 'Borders', + 'Border radius', + 'Shadows', + 'Cursors', + 'Opacity' + ]), + docSection('Colors', ['Text colors', 'Background colors']), + docSection('Icons'), + docSection('Buttons'), + docSection('Links'), + docSection('Forms', [ + 'Inputs & textareas', + 'Selects', + 'Ranges', + 'Checkboxes', + 'Radio buttons', + 'Switches', + 'Toggle group' + ]), + docSection('Tables'), + docSection('Animations'), + docSection('Triangles'), + docSection('Miscellaneous') + ] + }, + { + name: 'Examples', + href: '/examples/', + items: exampleLinks + }, + { name: 'Icons', href: '/icons' }, + { + name: 'Catalog', + href: '/catalog', + items: catalogLinks.map(section => ({ + name: section.name, + href: `/catalog#${section.id}` + })) + }, + { name: 'Layout Scales', href: '/layout-scales' } +]; + +export { nav, exampleLinks }; diff --git a/docs/catalog.md b/docs/catalog.md new file mode 100644 index 00000000..716086d8 --- /dev/null +++ b/docs/catalog.md @@ -0,0 +1,9 @@ +--- +title: Catalog +--- + + + + diff --git a/docs/documentation/animations.md b/docs/documentation/animations.md new file mode 100644 index 00000000..45567baa --- /dev/null +++ b/docs/documentation/animations.md @@ -0,0 +1,55 @@ +# Animations + +Animations and animation modifiers. + +```selectors +.animation-pulse +.animation-spin +.animation-fade-in +.animation-fade-out +.animation-fade-in-out +.animation-shake +``` + +Transition and reaction animations. By default, these run once for 1.5 seconds. + +```example +
+
+
+
+
+
+``` + +```selectors +.animation--speed-025 +.animation--speed-05 +.animation--speed-1 +.animation--speed-2 +.animation--speed-4 +.animation--delay +``` + +Animation duration modifiers. +Change animation duration with an `animation--speed-{seconds}` modifier. +Add a 1 second delay with `animation--delay`. + +```example +
+
+
+
+
+
+``` + +```selectors +.animation--infinite +``` + +Repeat an animation infinitely with a `animation--infinite` modifier. + +```example +
+``` diff --git a/docs/documentation/buttons.md b/docs/documentation/buttons.md new file mode 100644 index 00000000..8d3fa1a5 --- /dev/null +++ b/docs/documentation/buttons.md @@ -0,0 +1,117 @@ +# Buttons + +Although the `btn` class will apply button styling to any element, you should typically apply it to the ` + + + +
+ +
+``` + +```selectors +.btn--stroke +``` + +Modify a button so its text and borders are colored and its background transparent. +The border color and text color will always match. +To change the text and border color, use a `btn--{color}` modifier (`*-deep` colors are not available). + +```example + + + +
+ +
+``` + +```selectors +.btn--s +``` + +Make a button small. + +```example + +``` + +```selectors +.btn.is-active +.btn--stroke.is-active +``` + +Apply a darker active state to buttons by adding the `is-active` class. + +```example + + + + +``` + +```selectors +.btn:disabled +``` + +When a button has the `disabled` attribute, it will be styled accordingly. + +```example + +``` + +```selectors +.btn--pill-stroke +``` + +Create pill button groups. +Pill groups must be wrapped in a `inline-flex` container. +Every button in a pill group must have +- a `btn` class +- any `btn--*` modifiers you want (e.g. `btn--stroke`) +- a `btn--pill` *or* `btn--pill-stroke` modifier. Use the `btn--pill-stroke` modifier if the buttons in the pill group are mostly stroke buttons (though active buttons can be non-stroke, if you like that) +- a `btn--pill-*` modifier specifying its position (e.g. `btn--pill-hc`) + +For *horizontal* pills, use +- `inline-flex` on the container +- `btn btn--pill(-stroke) btn--pill-hc` (center) +- `btn btn--pill(-stroke) btn--pill-hl` (left) +- `btn btn--pill(-stroke) btn--pill-hr` (right) + +For *vertical* pills, use +- `inline-flex flex--column` on the container +- `btn btn--pill(-stroke) btn--pill-vc` (center) +- `btn btn--pill(-stroke) btn--pill-vt` (top) +- `btn btn--pill(-stroke) btn--pill-vb` (bottom) + +Pill buttons can be modified and themed in the same ways as regular buttons. + +```example +
+ + + +
+
+ + + +
+``` diff --git a/docs/documentation/colors.md b/docs/documentation/colors.md new file mode 100644 index 00000000..75a5abcc --- /dev/null +++ b/docs/documentation/colors.md @@ -0,0 +1,53 @@ +# Colors + +Background and text colors. Colors include dark, light, and faint variations. +Be sure to maintain a readable color contrast between background and text. +Only use `-faint` text colors on dark backgrounds. + +## Text colors + +```selectors +.color-{color} +``` + +Apply a text color with `color-{name}`. + +```color-grid color +``` + +```selectors +.color-{color}-on-hover +.color-{color}-on-active +``` + +Apply a text color on hover and active states. + +```example +
color-red-on-hover
+
color-red-on-active (not active)
+
color-red-on-active (active)
+``` + +## Background colors + +```selectors +.bg-{color} +``` + +Apply a background color with `bg-{name}`. + +```color-grid bg +``` + +```selectors +.bg-{color}-on-hover +.bg-{color}-on-active +``` + +Apply a background color on hover and active states. + +```example +
bg-darken25-on-hover
+
bg-darken25-on-active (not active)
+
bg-darken25-on-active (active)
+``` diff --git a/docs/documentation/forms.md b/docs/documentation/forms.md new file mode 100644 index 00000000..49f802a7 --- /dev/null +++ b/docs/documentation/forms.md @@ -0,0 +1,450 @@ +# Forms + +Styles for HTML5 input and textarea elements. All form elements fit into Assembly's `6px` baseline grid. + +## Inputs & textareas + +The `input` class styles input types `text`, `password`, `email`, +`datetime`, `search`, `tel`, `url`, and `number`. The `textarea` class styles the ` + +``` + +```selectors +.textarea--s +``` + +Make a textarea extra small. + +```example + +``` + +```selectors +.input:disabled +.textarea:disabled +.select--stroke:disabled +``` + +When `input` and `textarea` elements have a `disabled` attribute, they will be styled accordingly. + +```example + + +``` + +```selectors +.input[readonly] +.textarea[readonly] +``` + +When `input` and `textarea` elements have a `readonly` attribute, they will be styled accordingly. + +```example + + +``` + +## Selects + +The select component styles the `` element with the `select` class. +- A `
` with the `select-arrow` class. + +```selectors +.select-container +.select +.select-arrow +``` + +Style a select component. +Set the text and arrow color by adding a `select--{color}` modifier to the `select` element (`*-dark` colors are not available). + +```example +
+ +
+
+
+ +
+
+``` + +```selectors +.select--s +``` + +Make a select component small. + +```example +
+ +
+
+``` + +```selectors +.select--stroke +``` + +Modify a select component so that it has a border. +Set the border and arrow color by adding a `select--{color}` modifier to the `select` element along with the `select--stroke` modifier (`*-dark` colors are not available). +Note that the `select--stroke` modifier changes how the color modifier is interpreted. + +```example +
+ +
+
+
+ +
+
+``` + +```selectors +.select:disabled +``` + +When the `select` element has the `disabled` attribute, the component will be styled accordingly. + +```example +
+ +
+
+
+ +
+
+``` + +## Ranges + +The `range` class styles the `` element. The markup must fit the following pattern: +- A wrapping `
` with the `range` class. +- An `` element. + +```selectors +.range +``` + +Style a range element. + +The best way to vertically align range elements with other items on the same row is to wrap the row in a `flex flex--center-cross` container. +For more details, read about the [flexbox classes](/documentation/#flexbox). + +```example +
+ +
+``` + +```selectors +.range--s +``` + +Make a range element small. + +```example +
+ +
+``` + +```selectors +.range:disabled +``` + +When a range element has the `disabled` attribute, it will be styled accordingly. + +```example +
+ +
+``` + +## Checkboxes + +The checkbox component styles the `` element. The markup must fit the following pattern: +- A wrapping `
` with the class `checkbox` + containing an `` with the chosen icon (usually `#icon-check`). + +```selectors +.checkbox +``` + +Style a checkbox with a label. Change the color of the checkbox with a `checkbox--{color}` modifier (`*-dark` colors are not available). +Adjust the line height of the checkbox to match a label with small text with the `checkbox--s-label` modifier. + +```example +
+ +
+ More cats + + +``` + +```selectors +input:disabled + .checkbox +``` + +When a checkbox has the `disabled` attribute, it will be styled accordingly. + +```example + + +``` + +## Radio buttons + +The radio component styles the `` element. The markup must fit the following pattern: +- A wrapping `
` with the class `radio`. + +```selectors +.radio +``` + +Style a radio button. Change the color of the radio button with a `radio--{color}` modifier (`*-dark` colors are not available). +Adjust the line height of the radio button to match a label with small text with the `radio--s-label` modifier. + +```example + + +``` + +```selectors +input:disabled + .radio +``` + +When a radio button has the `disabled` attribute, it will be styled accordingly. + +```example + + +``` + +## Switches + +The switch component styles the `` element. The markup must fit the following pattern: +- A wrapping `
` with the class `switch`. + +```selectors +.switch +``` + +Style a switch. +Change the color of the dot when inactive, the border when inactive, +and the background when active with a `switch--{color}` modifier (`*-dark` colors are not available). +Change the color of the dot when active with a `switch--dot-{color}` modifier. Adjust the line height of the switch to match a label with small text with the `switch--s-label` modifier. + +```example + + +``` + +```selectors +.switch--l +``` + +Make a switch control large. + +```example + +``` + +```selectors +input:disabled + .switch +``` + +When a switch has the `disabled` attribute, it will be styled accordingly. + +```example + + +``` + +## Toggle group + +The toggle group component styles a set of `` elements. The markup must fit the following pattern: +- A wrapping `
` around all your radio options with the class `toggle-group`. +- For each radio option, a wrapping `
` with the class `toggle`. + +```selectors +.toggle-group +.toggle-container +.toggle +``` + +Style a toggle group. +Change the inactive toggle text color and the active toggle background color with a `toggle--{color}` modifier (`*-dark` colors are not available). +Change the active label color with a `toggle--active-{color}` modifier (`*-dark` colors are not available). +Change the border radiuses of toggle groups inside small text with `toggle-group--s` on the `toggle-container` element. + +```example +
+ + +
+``` + +```selectors +.toggle--s +``` + +Make a toggle small. + +```example +
+ + +
+``` + +```selectors +input:disabled + .toggle +``` + +When a toggle group input has the `disabled` attribute, it will be styled accordingly. + +```example +
+ + +
+``` diff --git a/docs/documentation/icons.md b/docs/documentation/icons.md new file mode 100644 index 00000000..35e7e23a --- /dev/null +++ b/docs/documentation/icons.md @@ -0,0 +1,23 @@ +# Icons + +Assembly uses an SVG icon sprite. To load the sprite, you must must include [`assembly.js`](https://labs.mapbox.com/assembly/) on your page. + +**Please read [the Icons page](/icons) to learn more about the available icons and the [Javascript API section of the home page](/#javascript-api) to learn how to use them.** + +```selectors +.icon +``` + +The `icon` class must be appled to an `` element. +Inside the ``, insert a `` element whose `xlink:href` attribute +points to an icon loaded by `assembly.js`. All icons are referenced +by the string `#icon-{name}`. By default, icons inherit size from their calculated text size. +Change the size of icons by applying `w{size}` and `h{size}` classes to the svg. + +Be aware that *icons are block-level*. + +To see a list of all available icons, visit [the icons page](/icons). + +```example + +``` diff --git a/docs/documentation/index.md b/docs/documentation/index.md new file mode 100644 index 00000000..517c3429 --- /dev/null +++ b/docs/documentation/index.md @@ -0,0 +1,27 @@ +--- +title: Documentation +--- + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/documentation/layout.md b/docs/documentation/layout.md new file mode 100644 index 00000000..7631d642 --- /dev/null +++ b/docs/documentation/layout.md @@ -0,0 +1,762 @@ +# Layout + +Define the size, shape, and content flow of your markup. + +## Grid + +Assembly uses a 12-column flexible grid with optional gutters. + +```selectors +.grid +.col +``` + +Grids require at minimum two elements: A parent with the class `grid` and a child with the class `col`. To define the width of `col` in a grid, add a width class, `col--auto`, or both. The `grid` class includes `*-mm`, `*-ml`, and `*-mxl` variations to target screen sizes. + +To avoid unexpected side-effects, do not add margin left or right to `grid` and do not add padding left or right to `col`. + +```example +
+
one
+
two
+
three
+
+``` + +```selectors +.col--auto +``` + +Apply to a `col` element to override a `col--{size}` rule and take on default `col`, occupying equal space with other `col` siblings. +This class is designed to be used with `*-mm`, `*-ml`, and `*-mxl` variations +to target screen sizes. + +```example +
+
w-1/4 col--auto-ml
+
w-3/4 col--auto-ml
+
+``` + +```selectors +.grid--gut0 +.grid--gut3 +.grid--gut6 +.grid--gut12 +.grid--gut18 +.grid--gut24 +.grid--gut30 +.grid--gut36 +.grid--gut60 +.grid--gut120 +``` + +Apply column gutters to all columns in a grid by adding a `grid--gut{size}` modifier to the `grid` element. Class set includes `*-mm`, `*-ml`, and `*-mxl` variations to target screen sizes. + +```example +
+
+
w-1/6
+
+
+
w-1/3
+
+
+
w-1/2
+
+
+``` + +## Display + +Control the display style of elements. + +```selectors +.inline +.block +.inline-block +.none +``` + +Set an element's `display` property value. Class set includes `*-mm`, `*-ml`, and `*-mxl` variations to target screen sizes. + +```example +
inline
+
block
+
inline-block
+
none
+``` + +## Positioning + +Classes for basic positioning. For flexbox positioning rules, see the documentation for [flexbox](#flexbox) utilities. + +```selectors +.fixed +.absolute +.relative +.static +.sticky +``` + +Set an element's `position` property value. Class set includes `*-mm`, `*-ml`, and `*-mxl` variations to target screen sizes. + +```example +
+
absolute
+
relative
+
static
+
static
+ +
+``` + +```selectors +.top +.right +.left +.bottom +``` + +Pin a positioned element against a side of its container. Positioning classes can be combined to stretch elements across the top, bottom, left, or right sides of their container. Class set includes `*-mm`, `*-ml`, and `*-mxl` variations to target screen sizes. + +```example +
+
right
+
bottom
+
left
+
top left right
+
+``` + +```selectors +.z-neg1 +.z0 +.z1 +.z2 +.z3 +.z4 +.z5 +``` + +Override default stacking order by setting an element's `z-index` property value. Whenever possible, rely on built-in stacking order and avoid using these classes. Class set includes `*-mm`, `*-ml`, and `*-mxl` variations to target screen sizes. + +```example +
+
z5
+
z1
+
z-neg1
+
+``` + +## Margins + +All margin classes fit the following pattern: `m`. +And all margin class sets include `*-mm`, `*-ml`, and `*-mxl` variations to target screen sizes. + +```selectors +.mx-auto +``` + +Set `auto` margins on left and right. This pattern is useful for horizontally centering block elements or aligning block elements to the start or end of their container. + +```example +
mx-auto
+
ml-auto
+
mr-auto
+``` + +```selectors +.my{margin} +``` + +Apply margin on the top and bottom. + +```example +
my24
+``` + +```selectors +.mx{margin} +``` + +Apply margin on the left and right. + +```example +
mx24
+``` + +```selectors +.mt{margin} +``` + +Apply margin on the top. + +```example +
mt24
+``` + +```selectors +.mr{margin} +``` + +Apply margin on the right. + +```example +
mr24
+``` + +```selectors +.mr-1/12 +.mr-1/6 +.mr-1/4 +.mr-1/3 +.mr-5/12 +.mr-1/2 +.mr-7/12 +.mr-2/3 +.mr-3/4 +.mr-5/6 +.mr-11/12 +``` + +Apply percentage-based margin on the right. When using with `col`, add a width class, `col--auto`, or both to define the size of the element. + +```example +
+
mr-1/2
+
+``` + +```selectors +.mb{margin} +``` + +Apply margin on the bottom. + +The negative margin classes, mb-neg, can be useful for certain design patterns like underline tabs. + +```example +
mb24
+``` + +```selectors +.ml{margin} +``` + +Apply margin on the left. + +```example +
ml24
+``` + +```selectors +.ml-1/12 +.ml-1/6 +.ml-1/4 +.ml-1/3 +.ml-5/12 +.ml-1/2 +.ml-7/12 +.ml-2/3 +.ml-3/4 +.ml-5/6 +.ml-11/12 +``` + +Apply percentage-based margin on the left. When using with `col`, add a width class, `col--auto`, or both to define the size of the element. + +```example +
+
ml-1/2
+
+``` + +## Padding + +All padding classes fit the following pattern: `m`. +And all padding class sets include `*-mm`, `*-ml`, and `*-mxl` variations to target screen sizes. + +```selectors +.py{margin} +``` + +Apply padding on the top and bottom. + +```example +
py24
+``` + +```selectors +.px{margin} +``` + +Apply padding on the left and right. + +```example +
px24
+``` + +```selectors +.pt{margin} +``` + +Apply padding on the top. + +```example +
pt24
+``` + +```selectors +.pr{margin} +``` + +Apply padding on the right. + +```example +
pr24
+``` + +```selectors +.pb{margin} +``` + +Apply padding on the bottom. + +```example +
pb24
+``` + +```selectors +.pl{margin} +``` + +Apply padding on the left. + +```example +
pl24
+``` + +## Sizing + +All sizing classes fit the following pattern: `` or `-full` for `100%`. +And all sizing class sets include `*-mm`, `*-ml`, and `*-mxl` variations to target screen sizes. + +```selectors +.w{width} +``` + +Set an element's width. + +In addition to numeric values, there are `w-full` and `w-auto` classes. + +```example +
w60
+``` + +```selectors +.w-1/12 +.w-1/6 +.w-1/4 +.w-1/3 +.w-5/12 +.w-1/2 +.w-7/12 +.w-2/3 +.w-3/4 +.w-5/6 +.w-11/12 +``` + +Set a percentage-based width. + +```example +
+
w-1/2
+
w-1/4
+
w-1/4
+
+``` + +```selectors +.wmax{maxWidth} +``` + +Set an element's maximum width. + +In addition to numeric values, there are `wmax-full` and `wmax-none` classes. + +```example +
wmax6
+``` + +```selectors +.wmin{width} +``` + +Set an element's minimum width. + +In addition to numeric values, there is the `wmin-full` class. + +```example +
wmin60
+``` + +```selectors +.h{width} +``` + +Set an element's height. + +In addition to numeric values, there are `h-full` and `h-auto` classes. + +```example +
h24
+``` + +```selectors +.hmax{maxWidth} +``` + +Set an element's maximum height. + +In addition to numeric values, there are `hmax-full` and `hmax-none` classes. + +```example +
hmax12
+``` + +```selectors +.hmin{width} +``` + +Set an element's minimum height. + +In addition to numeric values, there is the `hmin-full` class. + +```example +
hmin60
+``` + +```selectors +.h-viewport-full +.h-viewport-1/12 +.h-viewport-1/6 +.h-viewport-1/4 +.h-viewport-1/3 +.h-viewport-5/12 +.h-viewport-1/2 +.h-viewport-7/12 +.h-viewport-2/3 +.h-viewport-3/4 +.h-viewport-5/6 +.h-viewport-11/12 +``` + +Set a viewport-based height. + +```example +
h-viewport-1/3
+``` + +```selectors +.hmax-viewport-full +.hmax-viewport-1/12 +.hmax-viewport-1/6 +.hmax-viewport-1/4 +.hmax-viewport-1/3 +.hmax-viewport-5/12 +.hmax-viewport-1/2 +.hmax-viewport-7/12 +.hmax-viewport-2/3 +.hmax-viewport-3/4 +.hmax-viewport-5/6 +.hmax-viewport-11/12 +``` + +Set a viewport-based max width. + +```example +
hmax-viewport-1/3
+``` + +## Flexbox + +Flexbox utilities. All class sets include `*-mm`, `*-ml`, and `*-mxl` variations to target screen sizes. + +Usage must fit the following pattern: +- `flex` or `inline-flex` must be on a parent container in order to use `flex--*` modifiers, while `flex-child-*` rules control children of a `flex` or `inline-flex` element. +- By default, the `main` axis is horizontal and the `cross` axis is vertical. The axes can be inverted with the use of `flex--column`. +- To learn about how the flexbox system works, check out ["A Complete Guide to Flexbox"](https://css-tricks.com/snippets/css/a-guide-to-flexbox). + +```selectors +.flex +.inline-flex +``` + +Establish an element as a flex parent. +These classes allow the use of `flex--*` modifiers, and `flex-child-*` classes on children. + +```example +
flex
+
inline-flex
+``` + +```selectors +.flex--column +``` + +Set the direction of the main axis to top-to-bottom (default is left-to-right). + +```example +
+ 1 + 2 + 3 +
+``` + +```selectors +.flex--column-reverse +``` + +Set the direction of the main axis to bottom-to-top (default is left-to-right). + +```example +
+ 1 + 2 + 3 +
+``` + +```selectors +.flex--row +``` + +Set the direction of the main axis to left-to-right (the default value of `flex`). + +```example +
+ 1 + 2 + 3 +
+``` + +```selectors +.flex--row-reverse +``` + +Set the direction of the main axis to right-to-left (default is left-to-right). + +```example +
+ 1 + 2 + 3 +
+``` + +```selectors +.flex--center-main +``` + +Center an element's children on the main axis. + +```example +
+
child
+
+``` + +```selectors +.flex--center-cross +``` + +Center an element's children on the cross axis. + +```example +
+
child
+
+``` + +```selectors +.flex--start-cross +``` + +Align an element's children to the start of the cross axis. + +```example +
+
child
+
child
+
+``` + +```selectors +.flex--start-main +``` + +Align an element's children to the start of the main axis. + +```example +
+
child
+
+``` + +```selectors +.flex--end-cross +``` + +Align an element's children to the end of the cross axis. + +```example +
+
child
+
child
+
+``` + +```selectors +.flex--end-main +``` + +Align an element's children to the end of the main axis. + +```example +
+
child
+
+``` + +```selectors +.flex--wrap +``` + +Allow children to wrap. By default, they are all forced onto one line. + +```example +
+
child
+
child
+
+``` + +```selectors +.flex--stretch-cross +``` + +Stretch children to fill the parent container along the cross axis. + +```example +
+
child
+
+``` + +```selectors +.flex--space-between-main +``` + +Evenly distribute children across the line, so first child is at the start +and last child is at the end. + +```example +
+
child
+
child
+
+``` + +```selectors +.flex-child-grow +``` + +Make a child grow to fill whatever space is available in the main axis of the parent container. +This is useful when you have one or more elements of +fixed width, and another element that should take up the remaining space in the row. + +Typically, you will also want to apply the `flex-child-no-shrink` class to the element in the row with a fixed width. + +```example +
+
child
+
child
+
+``` + +```selectors +.flex-child-no-shrink +``` + +Prevent a child from shrinking below its width value. + +By default, flex children (even with specified widths) will shrink as needed to accommodate sibling elements. +This class prevents that default shrinkage, forcing siblings to accommodate the parent's width. + +```example +
+
child
+
child
+
child
+
+``` + +## Layout utils + +Extra layout utilities. + +```selectors +.bleed +.bleed-r +.bleed-l +.unbleed +``` + +Bleed utilities. Bleed classes allow an element inside a centered container to escape the container and meet the edge of the screen. +When using the bleed classes, consider adding the `.overflow-hidden` class on a parent element to prevent horizontal overflow. + +*Warning*: Bleeding elements will never bleed out to more than 50% of the viewport width. +So you should not make an element bleed to one side unless it's already on that half of +the screen. + +*Warning*: In Firefox, children with negative margins have different layout effects than in other browsers. +The bleed classes should only be used if the parent's width is stable, either because it is set or because other children determine it. + +Class set includes `*-mm`, `*-ml`, and `*-mxl` variations to target screen sizes. +These screen-size variants are best suited to *adding* the bleed, not *removing* it. +Be aware of the effects of the `unbleed` class: it resets `margin-left`, `margin-right`, +and `flex-child-grow`. + +```example +
+
+
Right bleed
+
+
+``` + +```selectors +.fl +.fr +.unfloat +.clearfix +``` + +Float utilities. Class set (except `clearfix`) includes `*-mm`, `*-ml`, and `*-mxl` variations to target screen sizes. + +```example +
+
left
+
+
with .clearfix
+
right
+
without .clearfix
+
unfloat
+``` diff --git a/docs/documentation/links.md b/docs/documentation/links.md new file mode 100644 index 00000000..4bdff25d --- /dev/null +++ b/docs/documentation/links.md @@ -0,0 +1,40 @@ +# Links + +Create clickable, hoverable, focusable, colored text. +These classes are often used for actual `
` elements with `href` attributes, +but might prove useful for ` + +``` diff --git a/docs/documentation/miscellaneous.md b/docs/documentation/miscellaneous.md new file mode 100644 index 00000000..5289be2e --- /dev/null +++ b/docs/documentation/miscellaneous.md @@ -0,0 +1,155 @@ +# Miscellaneous + +Extra rules that come in handy. + +```selectors +.scroll-styled +``` + +Style scrollbars. Only works in Webkit-based browsers (Safari and Chrome). +On dark backgrounds, add the `scroll-styled--dark` modifier. + +**Use in combination with `overflow-auto`.** + +```example +
+
+``` + +```selectors +.loading +.loading--dark +.loading--s +``` + +Style a loading spinner. +On dark backgrounds, add the `loading--dark` modifier. +For a smaller spinner, add the `loading--s` modifier. +Use `color-*` classes to apply a custom color to the spinner handle. + +```example +
+
+
+
+
+
+
+
+``` + +```selectors +.events-none +``` + +Disable touch and click events on an element and +its children. Be aware that this rule does not disable keyboard events, +so a user can still tab to an element and press Return to trigger +its click event. + +```example +
You can't click this
+``` + +```selectors +.events-all +``` + +Enable touch and click events on an element and +its children. Use to re-enable such events inside a parent with the `events-none` class. + +```example +
You can click this.
+``` + +```selectors +.unselectable +``` + +Disable text selection on an element and its children. Use only in cases where accidental text selection causes user frustration. + +```example +
You can't select this.
+``` + +```selectors +.selectable +``` + +Enable text selection on an element and its children. +Use to re-enable selection inside a parent with the `unselectable` class. + +```example +
You can select this.
+``` + +```selectors +.transition +``` + +Apply a subtle animated transition when changing CSS properties on an element. See MDN's [list of CSS animated properties](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animated_properties) for every property effected by this class. + +```example +
Hover over me
+``` + +```selectors +.transition-none +``` + +Disable an animated transition on elements like [`btn`](/documentation/#buttons) that have default transitions. + +```example +
transition-none
+``` + +```selectors +.overflow-scroll +``` + +Always show scrollbars. + +```example +
Curabitur blandit tempus porttitor. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Etiam porta sem malesuada magna mollis euismod. Maecenas faucibus mollis interdum. Donec ullamcorper nulla non metus auctor fringilla. Nullam id dolor id nibh ultricies vehicula ut id elit.
+``` + +```selectors +.overflow-auto +``` + +Display scrollbars if content flows beyond the edge of the element. + +```example +
Curabitur blandit tempus porttitor. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Etiam porta sem malesuada magna mollis euismod. Maecenas faucibus mollis interdum. Donec ullamcorper nulla non metus auctor fringilla. Nullam id dolor id nibh ultricies vehicula ut id elit.
+``` + +```selectors +.overflow-hidden +``` + +Hide content if it extends beyond the edge of the containing element. + +```example +
Curabitur blandit tempus porttitor. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Etiam porta sem malesuada magna mollis euismod. Maecenas faucibus mollis interdum. Donec ullamcorper nulla non metus auctor fringilla. Nullam id dolor id nibh ultricies vehicula ut id elit.
+``` + +```selectors +.hide-visually +``` + +Hide an element *visually*, but keep it available to screen readers. + +```example +
You cannot see this with your eyes.
+
There is a sentence above this that you cannot see.
+``` + +```selectors +.none-print +``` + +Apply display none when rendering page for print. + +```example + +``` + +```selectors +.unprose +``` + +Remove prose styling. Useful when you need to display, for example, a custom form inside an otherwise consistently-styled document. + +**Apply this class directly to the element you want to unstyle.** It does not work on containers, only on the elements themselves. + +```example +
+

felis euismod semper

+ Click me +

Morbi leo risus

+ Click me +
+``` + +```selectors +.prose--dark +``` + +Invert the coloring of textual content inside `prose` sections, so it is legible against dark backgrounds. + +```example +
+

Tortor Porta

+ Aenean lacinia bibendum nulla sed consectetur. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam quis risus eget urna mollis ornare vel eu leo. +
+

Cras justo odio, dapibus ac facilisis[note] in, egestas eget quam. Lorem ipsum dolor sit amet, consectetur adipiscing elit. H20 electrolitis.

+
+``` diff --git a/docs/examples/badges.md b/docs/examples/badges.md new file mode 100644 index 00000000..c422a0aa --- /dev/null +++ b/docs/examples/badges.md @@ -0,0 +1,37 @@ +

Badges

+ +

Badges

+ +```example +
+ screamin dealz +
+``` + +```example +
+ + Did you know? +
+``` + +```example +
+ Heads up! + +
+``` + +```example +
+
+ This +
+ +
+ is that +
+
+``` diff --git a/docs/examples/equal-height-columns.md b/docs/examples/equal-height-columns.md new file mode 100644 index 00000000..d5d9269f --- /dev/null +++ b/docs/examples/equal-height-columns.md @@ -0,0 +1,31 @@ +

Equal-height columns

+ +

Equal-height columns

+ +
+ +This effect is easy, of course, if you can fix the height of the columns. Often, though, that's not what you want. If you want columns to have equal heights *based on the column with the tallest content*, you need some flexbox powers. + +The key here is to add `flex--stretch-cross` to the `grid`, and add `h-full` to the *child* of the `col` element, which is the one with the background color. + +
+ +```example +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+``` diff --git a/docs/examples/index.md b/docs/examples/index.md new file mode 100644 index 00000000..e56f52b0 --- /dev/null +++ b/docs/examples/index.md @@ -0,0 +1,9 @@ +--- +title: Examples +--- + + + + diff --git a/docs/examples/inputs.md b/docs/examples/inputs.md new file mode 100644 index 00000000..128c85e6 --- /dev/null +++ b/docs/examples/inputs.md @@ -0,0 +1,32 @@ +

Inputs

+ +

Input with button

+ +```example +
+ + +
+``` + +

Input with icon

+ +```example +
+
+ +
+ +
+``` + +

Input with spinner

+ +```example +
+
+ +
+ +
+``` diff --git a/site/pages/examples/legends.js b/docs/examples/legends.md similarity index 67% rename from site/pages/examples/legends.js rename to docs/examples/legends.md index f25e4ff8..46882599 100644 --- a/site/pages/examples/legends.js +++ b/docs/examples/legends.md @@ -1,8 +1,9 @@ -import React from 'react'; -import { HtmlExample } from '../../html_example'; -import { Page } from '../../page'; +

Legends

-const basicLegend = `
+

Legend with bars

+ +```example +
Title description
@@ -18,9 +19,11 @@ const basicLegend = `
Low
High
-
`; +
+``` -const basicLegendTwo = `
+```example +
Title description
@@ -34,9 +37,13 @@ const basicLegendTwo = `
50
75
-
`; +
+``` + +

Legend with varying radius

-const radiusLegend = `
+```example +
Title description
@@ -62,24 +69,5 @@ const radiusLegend = `
18
24
-
`; - -export default class ExampleLegends extends React.Component { - render() { - return ( - -

- Legend with bars -

-
- -
- -

- Legend with varying radius -

- -
- ); - } -} +
+``` diff --git a/site/pages/examples/modal.js b/docs/examples/modal.md similarity index 55% rename from site/pages/examples/modal.js rename to docs/examples/modal.md index 4cc0500b..21fa6161 100644 --- a/site/pages/examples/modal.js +++ b/docs/examples/modal.md @@ -1,8 +1,9 @@ -import React from 'react'; -import { HtmlExample } from '../../html_example'; -import { Page } from '../../page'; +

Modal

-const tiles = `
+

Modal component

+ +```example +
@@ -18,17 +19,5 @@ const tiles = `
-
`; - -export default class ExampleModal extends React.Component { - render() { - return ( - -

- Modal component -

- -
- ); - } -} +
+``` diff --git a/site/pages/examples/navigation.js b/docs/examples/navigation.md similarity index 60% rename from site/pages/examples/navigation.js rename to docs/examples/navigation.md index 5d947117..f9d32b7b 100644 --- a/site/pages/examples/navigation.js +++ b/docs/examples/navigation.md @@ -1,8 +1,9 @@ -import React from 'react'; -import { HtmlExample } from '../../html_example'; -import { Page } from '../../page'; +

Navigation

-const menuNavigation = `