@cobalt-ui/plugin-css
Advanced tools
Comparing version 0.8.0 to 0.8.1
# @cobalt-ui/plugin-css | ||
## 0.8.1 | ||
### Patch Changes | ||
- Updated dependencies [160200f] | ||
- @cobalt-ui/cli@0.6.0 | ||
## 0.8.0 | ||
@@ -4,0 +11,0 @@ |
@@ -14,3 +14,3 @@ import type { ParsedColorToken, ParsedCubicBezierToken, ParsedDimensionToken, ParsedDurationToken, ParsedFontToken, ParsedGradientToken, ParsedLinkToken, ParsedShadowToken, ParsedToken, ParsedTransitionToken, ParsedTypographyToken, Plugin } from '@cobalt-ui/core'; | ||
} | ||
export default function css(options?: Options): Plugin; | ||
export default function pluginCSS(options?: Options): Plugin; | ||
/** transform color */ | ||
@@ -17,0 +17,0 @@ export declare function transformColor(value: ParsedColorToken['$value']): string; |
import color from 'better-color-tools'; | ||
import { Indenter, kebabinate, FG_YELLOW, RESET } from '@cobalt-ui/utils'; | ||
import { indent, kebabinate, FG_YELLOW, RESET } from '@cobalt-ui/utils'; | ||
import { encode, formatFontNames } from './util.js'; | ||
@@ -9,16 +9,15 @@ const DASH_PREFIX_RE = /^-+/; | ||
const HEX_RE = /#[0-9a-f]{3,8}/g; | ||
export default function css(options) { | ||
export default function pluginCSS(options) { | ||
let config; | ||
let filename = options?.filename || './tokens.css'; | ||
let prefix = options?.prefix ? `${options.prefix.replace(DASH_PREFIX_RE, '').replace(DASH_SUFFIX_RE, '')}-` : ''; | ||
const i = new Indenter(); | ||
function makeVars(tokens, indentLv = 0, generateRoot = false) { | ||
const output = []; | ||
if (generateRoot) | ||
output.push(i.indent(':root {', indentLv)); | ||
output.push(indent(':root {', indentLv)); | ||
for (const [id, value] of Object.entries(tokens)) { | ||
output.push(i.indent(`--${prefix}${id.replace(DOT_UNDER_GLOB_RE, '-')}: ${value};`, indentLv + (generateRoot ? 1 : 0))); | ||
output.push(indent(`--${prefix}${id.replace(DOT_UNDER_GLOB_RE, '-')}: ${value};`, indentLv + (generateRoot ? 1 : 0))); | ||
} | ||
if (generateRoot) | ||
output.push(i.indent('}', indentLv)); | ||
output.push(indent('}', indentLv)); | ||
return output; | ||
@@ -54,4 +53,5 @@ } | ||
// transformation (1 pass through all tokens + modes) | ||
// note: async transforms aren’t documented, but allow it just in case a user needs it | ||
for (const token of tokens) { | ||
let value = (typeof options?.transform === 'function' && options.transform(token)) || defaultTransformer(token); | ||
let value = (typeof options?.transform === 'function' && (await options.transform(token))) || defaultTransformer(token); | ||
switch (token.$type) { | ||
@@ -87,3 +87,3 @@ case 'link': { | ||
modeVals[selector] = {}; | ||
let modeVal = (typeof options?.transform === 'function' && options.transform(token, modeName)) || defaultTransformer(token, modeName); | ||
let modeVal = (typeof options?.transform === 'function' && (await options.transform(token, modeName))) || defaultTransformer(token, modeName); | ||
switch (token.$type) { | ||
@@ -114,5 +114,4 @@ case 'link': { | ||
code.push('/**'); | ||
if (metadata.name) | ||
code.push(` * ${metadata.name}`); | ||
code.push(' * This file was auto-generated from tokens.json.'); | ||
code.push(` * ${metadata.name || 'Design Tokens'}`); | ||
code.push(' * Autogenerated from tokens.json.'); | ||
code.push(' * DO NOT EDIT!'); | ||
@@ -139,3 +138,3 @@ code.push(' */'); | ||
code.push(''); | ||
code.push(i.indent(`@supports (color: color(display-p3 1 1 1)) {`, 0)); // note: @media (color-gamut: p3) is problematic in most browsers | ||
code.push(indent(`@supports (color: color(display-p3 1 1 1)) {`, 0)); // note: @media (color-gamut: p3) is problematic in most browsers | ||
code.push(...makeP3(makeVars(tokenVals, 1, true))); | ||
@@ -145,7 +144,7 @@ for (const selector of selectors) { | ||
const wrapper = selector.trim().replace(SELECTOR_BRACKET_RE, ''); | ||
code.push(i.indent(`${wrapper} {`, 1)); | ||
code.push(indent(`${wrapper} {`, 1)); | ||
code.push(...makeP3(makeVars(modeVals[selector], 2, wrapper.startsWith('@')))); | ||
code.push(i.indent('}', 1)); | ||
code.push(indent('}', 1)); | ||
} | ||
code.push(i.indent('}', 0)); | ||
code.push(indent('}', 0)); | ||
} | ||
@@ -152,0 +151,0 @@ code.push(''); |
{ | ||
"name": "@cobalt-ui/plugin-css", | ||
"description": "Generate CSS from your design tokens schema (requires @cobalt-ui/cli)", | ||
"version": "0.8.0", | ||
"version": "0.8.1", | ||
"author": { | ||
@@ -19,2 +19,5 @@ "name": "Drew Powers", | ||
"types": "./dist/index.d.ts", | ||
"peerDependencies": { | ||
"@cobalt-ui/cli": "*" | ||
}, | ||
"dependencies": { | ||
@@ -27,7 +30,7 @@ "@cobalt-ui/utils": "^0.5.0", | ||
"devDependencies": { | ||
"@cobalt-ui/cli": "^0.5.2", | ||
"@cobalt-ui/core": "^0.6.2", | ||
"@cobalt-ui/cli": "^0.6.0", | ||
"@cobalt-ui/core": "^0.6.3", | ||
"@types/mime": "^2.0.3", | ||
"@types/svgo": "^2.6.4", | ||
"vitest": "^0.24.0" | ||
"vitest": "^0.24.3" | ||
}, | ||
@@ -34,0 +37,0 @@ "scripts": { |
@@ -15,8 +15,28 @@ # @cobalt-ui/plugin-css | ||
// tokens.config.mjs | ||
import css from '@cobalt-ui/plugin-css'; | ||
import pluginCSS from '@cobalt-ui/plugin-css'; | ||
/** @type import('@cobalt-ui/core').Config */ | ||
export default { | ||
plugins: [pluginCSS()], | ||
}; | ||
``` | ||
## Usage | ||
Running `npx co build` with the plugin set up will generate a `tokens/tokens.css` file. Inspect that, and import where desired and use the [CSS Custom Properties](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties) as desired ([docs](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties)). | ||
## Options | ||
### All Options | ||
Here are all plugin options, along with their default values | ||
```js | ||
// tokens.config.mjs | ||
import pluginCSS from '@cobalt-ui/plugin-css'; | ||
/** @type import('@cobalt-ui/core').Config */ | ||
export default { | ||
plugins: [ | ||
css({ | ||
pluginCSS({ | ||
/** set the filename inside outDir */ | ||
@@ -30,8 +50,4 @@ filename: './tokens.css', | ||
embedFiles: false, | ||
/** handle specific token types */ | ||
transform: { | ||
color: (value, token) => { | ||
return value; | ||
}, | ||
}, | ||
/** (optional) transform specific token values */ | ||
transform: () => null, | ||
/** (optional) prefix variable names */ | ||
@@ -44,8 +60,2 @@ prefix: '--my-prefix', | ||
## Usage | ||
Running `npx co build` with the plugin set up will generate a `tokens/tokens.css` file. Inspect that, and import where desired and use the [CSS Custom Properties](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties) as desired ([docs](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties)). | ||
## Config | ||
### Embed Files | ||
@@ -112,3 +122,4 @@ | ||
'color.base#light': ['body[data-color-mode="light"]'], | ||
'color.base#dark': ['body[data-color-mode="dark"]'], | ||
'color.base#dark': ['body[data-color-mode="dark"]', '@media (prefers-color-scheme:dark)'], | ||
'transition#reduced': ['@media (prefers-reduced-motion)'], | ||
}, | ||
@@ -196,3 +207,3 @@ }), | ||
Inside plugin options, you can specify an optional `transform()` function: | ||
Inside plugin options, you can specify an optional `transform()` function. | ||
@@ -205,5 +216,6 @@ ```js | ||
transform(token, mode) { | ||
// Replace "sans-serif" with "Brand Sans" for font tokens | ||
const oldFont = 'sans-serif'; | ||
const newFont = 'Custom Sans'; | ||
if (token.$type === 'font') { | ||
return token.$value.replace('sans-serif', 'Brand Sans'); | ||
return token.$value.map((value) => (value === oldFont ? newFont : value)); | ||
} | ||
@@ -216,3 +228,3 @@ }, | ||
Your transform will only take place if you return a string; otherwise the default transformer will take place. | ||
Your transform will only take place if you return a truthy value, otherwise the default transformer will take place. | ||
@@ -219,0 +231,0 @@ #### Custom tokens |
@@ -19,3 +19,3 @@ import type { | ||
import color from 'better-color-tools'; | ||
import {Indenter, kebabinate, FG_YELLOW, RESET} from '@cobalt-ui/utils'; | ||
import {indent, kebabinate, FG_YELLOW, RESET} from '@cobalt-ui/utils'; | ||
import {encode, formatFontNames} from './util.js'; | ||
@@ -42,3 +42,3 @@ | ||
export default function css(options?: Options): Plugin { | ||
export default function pluginCSS(options?: Options): Plugin { | ||
let config: ResolvedConfig; | ||
@@ -48,11 +48,9 @@ let filename = options?.filename || './tokens.css'; | ||
const i = new Indenter(); | ||
function makeVars(tokens: Record<string, any>, indentLv = 0, generateRoot = false): string[] { | ||
const output: string[] = []; | ||
if (generateRoot) output.push(i.indent(':root {', indentLv)); | ||
if (generateRoot) output.push(indent(':root {', indentLv)); | ||
for (const [id, value] of Object.entries(tokens)) { | ||
output.push(i.indent(`--${prefix}${id.replace(DOT_UNDER_GLOB_RE, '-')}: ${value};`, indentLv + (generateRoot ? 1 : 0))); | ||
output.push(indent(`--${prefix}${id.replace(DOT_UNDER_GLOB_RE, '-')}: ${value};`, indentLv + (generateRoot ? 1 : 0))); | ||
} | ||
if (generateRoot) output.push(i.indent('}', indentLv)); | ||
if (generateRoot) output.push(indent('}', indentLv)); | ||
return output; | ||
@@ -90,4 +88,5 @@ } | ||
// transformation (1 pass through all tokens + modes) | ||
// note: async transforms aren’t documented, but allow it just in case a user needs it | ||
for (const token of tokens) { | ||
let value = (typeof options?.transform === 'function' && options.transform(token)) || defaultTransformer(token); | ||
let value = (typeof options?.transform === 'function' && (await options.transform(token))) || defaultTransformer(token); | ||
switch (token.$type) { | ||
@@ -119,3 +118,3 @@ case 'link': { | ||
if (!modeVals[selector]) modeVals[selector] = {}; | ||
let modeVal = (typeof options?.transform === 'function' && options.transform(token, modeName)) || defaultTransformer(token, modeName); | ||
let modeVal = (typeof options?.transform === 'function' && (await options.transform(token, modeName))) || defaultTransformer(token, modeName); | ||
switch (token.$type) { | ||
@@ -146,4 +145,4 @@ case 'link': { | ||
code.push('/**'); | ||
if (metadata.name) code.push(` * ${metadata.name}`); | ||
code.push(' * This file was auto-generated from tokens.json.'); | ||
code.push(` * ${metadata.name || 'Design Tokens'}`); | ||
code.push(' * Autogenerated from tokens.json.'); | ||
code.push(' * DO NOT EDIT!'); | ||
@@ -171,3 +170,3 @@ code.push(' */'); | ||
code.push(''); | ||
code.push(i.indent(`@supports (color: color(display-p3 1 1 1)) {`, 0)); // note: @media (color-gamut: p3) is problematic in most browsers | ||
code.push(indent(`@supports (color: color(display-p3 1 1 1)) {`, 0)); // note: @media (color-gamut: p3) is problematic in most browsers | ||
code.push(...makeP3(makeVars(tokenVals, 1, true))); | ||
@@ -177,7 +176,7 @@ for (const selector of selectors) { | ||
const wrapper = selector.trim().replace(SELECTOR_BRACKET_RE, ''); | ||
code.push(i.indent(`${wrapper} {`, 1)); | ||
code.push(indent(`${wrapper} {`, 1)); | ||
code.push(...makeP3(makeVars(modeVals[selector], 2, wrapper.startsWith('@')))); | ||
code.push(i.indent('}', 1)); | ||
code.push(indent('}', 1)); | ||
} | ||
code.push(i.indent('}', 0)); | ||
code.push(indent('}', 0)); | ||
} | ||
@@ -238,3 +237,3 @@ | ||
for (const [k, v] of Object.entries(value)) { | ||
values[kebabinate(k)] = Array.isArray(v) ? formatFontNames(v) : (v as ParsedTypographyToken['$value']); | ||
values[kebabinate(k)] = Array.isArray(v) ? formatFontNames(v) : (v as any); | ||
} | ||
@@ -241,0 +240,0 @@ return values; |
import {build} from '@cobalt-ui/cli/dist/build.js'; | ||
import fs from 'fs'; | ||
import {expect, test} from 'vitest'; | ||
import {describe, expect, test} from 'vitest'; | ||
import pluginCSS from '../dist/index.js'; | ||
const FIXTURES_DIR = new URL('./fixtures/', import.meta.url); | ||
for (const name of fs.readdirSync(FIXTURES_DIR)) { | ||
test(name, async () => { | ||
const base = new URL(`./${name}/`, FIXTURES_DIR); | ||
const outDir = new URL('./dist/', base); | ||
const given = JSON.parse(fs.readFileSync(new URL('./given.json', base))); | ||
await build(given, { | ||
outDir, | ||
describe('@cobalt-ui/plugin-css', () => { | ||
test.each(['color', 'typography'])('%', async (dir) => { | ||
const cwd = new URL(`./${dir}/`, import.meta.url); | ||
const tokens = JSON.parse(fs.readFileSync(new URL('./tokens.json', cwd))); | ||
await build(tokens, { | ||
outDir: cwd, | ||
plugins: [ | ||
pluginCSS({ | ||
filename: 'actual.css', | ||
modeSelectors: { | ||
@@ -30,6 +28,4 @@ 'color#light': ['[data-color-theme="light"]'], | ||
const got = fs.readFileSync(new URL('./tokens.css', outDir), 'utf8'); | ||
const want = fs.readFileSync(new URL('./want.css', base), 'utf8'); | ||
expect(got).to.equal(want); | ||
expect(fs.readFileSync(new URL('./actual.css', cwd), 'utf8')).toBe(fs.readFileSync(new URL('./want.css', cwd), 'utf8')); | ||
}); | ||
} | ||
}); |
Sorry, the diff of this file is not supported yet
53136
245
5
793