├── .changeset
├── README.md
└── config.json
├── .git-blame-ignore-revs
├── .github
├── ISSUE_TEMPLATE
│ ├── bug.yml
│ └── feat.yml
├── pull_request_template.md
└── workflows
│ ├── ci.yml
│ └── release.yml
├── .gitignore
├── .nvmrc
├── .prettierignore
├── .vscode
├── extensions.json
└── settings.json
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── biome.json
├── package.json
├── packages
├── cli
│ ├── .gitignore
│ ├── .npmignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── bin
│ │ └── cli.js
│ ├── biome.json
│ ├── index.html
│ ├── lab.tsx
│ ├── package.json
│ ├── rolldown.config.ts
│ ├── src
│ │ ├── build.ts
│ │ ├── check.ts
│ │ ├── help.ts
│ │ ├── index.ts
│ │ ├── init.ts
│ │ ├── lab.ts
│ │ ├── normalize.ts
│ │ ├── shared.ts
│ │ └── version.ts
│ ├── terrazzo.config.mjs
│ ├── test
│ │ ├── build.test.ts
│ │ ├── check.test.ts
│ │ ├── fixtures
│ │ │ ├── check-config
│ │ │ │ ├── styles
│ │ │ │ │ └── tokens.json
│ │ │ │ └── terrazzo.config.js
│ │ │ ├── check-invalid
│ │ │ │ └── tokens.json
│ │ │ ├── check-npm
│ │ │ │ ├── .gitignore
│ │ │ │ ├── node_modules
│ │ │ │ │ ├── .package-lock.json
│ │ │ │ │ ├── my-other-tokens
│ │ │ │ │ └── my-tokens
│ │ │ │ ├── package-lock.json
│ │ │ │ ├── package.json
│ │ │ │ ├── pkg-exports
│ │ │ │ │ ├── package.json
│ │ │ │ │ └── tokens-xyz.json
│ │ │ │ ├── pkg
│ │ │ │ │ ├── package.json
│ │ │ │ │ └── tokens.json
│ │ │ │ └── terrazzo.config.js
│ │ │ ├── check-valid
│ │ │ │ └── tokens.json
│ │ │ ├── error-no-default-export
│ │ │ │ └── terrazzo.config.js
│ │ │ ├── error-no-tokens
│ │ │ │ └── terrazzo.config.js
│ │ │ └── normalize
│ │ │ │ ├── input.json
│ │ │ │ └── want.json
│ │ ├── normalize.test.ts
│ │ └── version.test.ts
│ ├── tokens-example.json
│ ├── tsconfig.json
│ ├── vite.config.ts
│ └── vitest.config.ts
├── fonts
│ ├── FragmentMono-Italic.woff2
│ ├── FragmentMono-Regular.woff2
│ ├── InstrumentSans-Italic-VF.woff2
│ ├── InstrumentSans-VF.woff2
│ ├── LICENSE
│ ├── README.md
│ ├── biome.json
│ ├── fragment-mono.css
│ ├── instrument-sans.css
│ └── package.json
├── icons
│ ├── .gitignore
│ ├── .npmignore
│ ├── README.md
│ ├── biome.json
│ ├── package.json
│ ├── rollup.config.js
│ ├── src
│ │ └── index.tsx
│ └── tsconfig.json
├── parser
│ ├── .npmignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── biome.json
│ ├── package.json
│ ├── rolldown.config.ts
│ ├── src
│ │ ├── build
│ │ │ └── index.ts
│ │ ├── config.ts
│ │ ├── index.ts
│ │ ├── lib
│ │ │ └── code-frame.ts
│ │ ├── lint
│ │ │ ├── index.ts
│ │ │ └── plugin-core
│ │ │ │ ├── index.ts
│ │ │ │ ├── lib
│ │ │ │ └── docs.ts
│ │ │ │ └── rules
│ │ │ │ ├── a11y-min-contrast.ts
│ │ │ │ ├── a11y-min-font-size.ts
│ │ │ │ ├── colorspace.ts
│ │ │ │ ├── consistent-naming.ts
│ │ │ │ ├── descriptions.ts
│ │ │ │ ├── duplicate-values.ts
│ │ │ │ ├── max-gamut.ts
│ │ │ │ ├── required-children.ts
│ │ │ │ ├── required-modes.ts
│ │ │ │ └── required-typography-properties.ts
│ │ ├── logger.ts
│ │ ├── parse
│ │ │ ├── alias.ts
│ │ │ ├── index.ts
│ │ │ ├── json.ts
│ │ │ ├── normalize.ts
│ │ │ └── validate.ts
│ │ └── types.ts
│ ├── test
│ │ ├── config.test.ts
│ │ ├── lint.test.ts
│ │ └── parse.test.ts
│ ├── tsconfig.json
│ └── vitest.config.ts
├── plugin-css
│ ├── .gitignore
│ ├── .npmignore
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── biome.json
│ ├── package.json
│ ├── rolldown.config.ts
│ ├── src
│ │ ├── build
│ │ │ ├── index.ts
│ │ │ └── utility-css.ts
│ │ ├── index.ts
│ │ └── lib.ts
│ ├── test
│ │ ├── cli.test.ts
│ │ ├── fixtures
│ │ │ ├── base-selector
│ │ │ │ ├── tokens.json
│ │ │ │ └── want.css
│ │ │ ├── build-default
│ │ │ │ └── terrazzo.config.js
│ │ │ ├── chained-selector
│ │ │ │ ├── tokens.json
│ │ │ │ └── want.css
│ │ │ ├── cli-config-outdir
│ │ │ │ ├── .gitignore
│ │ │ │ ├── styles
│ │ │ │ │ ├── out
│ │ │ │ │ │ └── want.css
│ │ │ │ │ └── tokens.json
│ │ │ │ └── terrazzo.config.js
│ │ │ ├── cli-skip-build
│ │ │ │ ├── terrazzo.config.js
│ │ │ │ └── tokens.json
│ │ │ ├── cli-watch
│ │ │ │ ├── terrazzo.config.js
│ │ │ │ └── tokens.json
│ │ │ ├── cli-yaml
│ │ │ │ ├── terrazzo.config.js
│ │ │ │ └── tokens.yaml
│ │ │ ├── cli
│ │ │ │ ├── terrazzo.config.js
│ │ │ │ └── tokens.json
│ │ │ ├── ds-adobe-spectrum
│ │ │ │ └── want.css
│ │ │ ├── ds-apple-hig
│ │ │ │ └── want.css
│ │ │ ├── ds-figma-sds
│ │ │ │ └── want.css
│ │ │ ├── ds-github-primer
│ │ │ │ └── want.css
│ │ │ ├── ds-ibm-carbon
│ │ │ │ └── want.css
│ │ │ ├── ds-microsoft-fluent
│ │ │ │ └── want.css
│ │ │ ├── ds-radix
│ │ │ │ └── want.css
│ │ │ ├── ds-salesforce-lightning
│ │ │ │ └── want.css
│ │ │ ├── ds-shopify-polaris
│ │ │ │ └── want.css
│ │ │ ├── hex
│ │ │ │ ├── tokens.json
│ │ │ │ └── want.css
│ │ │ ├── type-boolean
│ │ │ │ ├── tokens.json
│ │ │ │ └── want.css
│ │ │ ├── type-border
│ │ │ │ ├── tokens.json
│ │ │ │ └── want.css
│ │ │ ├── type-color
│ │ │ │ ├── tokens.json
│ │ │ │ └── want.css
│ │ │ ├── type-dimension
│ │ │ │ ├── tokens.json
│ │ │ │ └── want.css
│ │ │ ├── type-gradient
│ │ │ │ ├── tokens.json
│ │ │ │ └── want.css
│ │ │ ├── type-shadow
│ │ │ │ ├── tokens.json
│ │ │ │ └── want.css
│ │ │ ├── type-string
│ │ │ │ ├── tokens.json
│ │ │ │ └── want.css
│ │ │ ├── type-transition
│ │ │ │ ├── tokens.json
│ │ │ │ └── want.css
│ │ │ ├── type-typography
│ │ │ │ ├── tokens.json
│ │ │ │ └── want.css
│ │ │ └── utility-css
│ │ │ │ ├── tokens.json
│ │ │ │ └── want.css
│ │ ├── js.test.ts
│ │ └── lib.test.ts
│ ├── tsconfig.json
│ └── vitest.config.ts
├── plugin-js
│ ├── .npmignore
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── biome.json
│ ├── package.json
│ ├── rolldown.config.ts
│ ├── src
│ │ ├── build.ts
│ │ ├── index.ts
│ │ └── lib.ts
│ ├── test
│ │ ├── border
│ │ │ ├── tokens.json
│ │ │ ├── want.d.ts
│ │ │ └── want.js
│ │ ├── color
│ │ │ ├── tokens.json
│ │ │ ├── want.d.ts
│ │ │ └── want.js
│ │ ├── index.test.ts
│ │ ├── shadow
│ │ │ ├── tokens.json
│ │ │ ├── want.d.ts
│ │ │ └── want.js
│ │ ├── transition
│ │ │ ├── tokens.json
│ │ │ ├── want.d.ts
│ │ │ └── want.js
│ │ └── typography
│ │ │ ├── tokens.json
│ │ │ ├── want.d.ts
│ │ │ └── want.js
│ ├── tsconfig.json
│ └── vitest.config.ts
├── plugin-sass
│ ├── .npmignore
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── biome.json
│ ├── package.json
│ ├── rolldown.config.ts
│ ├── src
│ │ ├── build.ts
│ │ ├── index.ts
│ │ └── lib.ts
│ ├── test
│ │ ├── fixtures
│ │ │ └── basic
│ │ │ │ ├── tokens.json
│ │ │ │ ├── want.css
│ │ │ │ └── want.scss
│ │ └── index.test.ts
│ ├── tsconfig.json
│ └── vitest.config.ts
├── plugin-swift
│ ├── .gitignore
│ ├── .npmignore
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── biome.json
│ ├── package.json
│ ├── rolldown.config.ts
│ ├── src
│ │ └── index.ts
│ ├── test
│ │ ├── color
│ │ │ ├── Want.xcassets
│ │ │ │ ├── color.primitive.blue.1.colorset
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── color.primitive.blue.10.colorset
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── color.primitive.blue.11.colorset
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── color.primitive.blue.12.colorset
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── color.primitive.blue.2.colorset
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── color.primitive.blue.3.colorset
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── color.primitive.blue.4.colorset
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── color.primitive.blue.5.colorset
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── color.primitive.blue.6.colorset
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── color.primitive.blue.7.colorset
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── color.primitive.blue.8.colorset
│ │ │ │ │ └── Contents.json
│ │ │ │ └── color.primitive.blue.9.colorset
│ │ │ │ │ └── Contents.json
│ │ │ └── tokens.json
│ │ └── index.test.ts
│ └── tsconfig.json
├── plugin-tailwind
│ ├── .gitignore
│ ├── .npmignore
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── biome.json
│ ├── package.json
│ ├── rolldown.config.ts
│ ├── src
│ │ ├── index.ts
│ │ └── lib.ts
│ ├── test
│ │ ├── cli.test.ts
│ │ └── fixtures
│ │ │ └── primer
│ │ │ ├── terrazzo.config.js
│ │ │ ├── tokens.json
│ │ │ └── want.css
│ ├── tsconfig.json
│ └── vitest.config.ts
├── react-color-picker
│ ├── .npmignore
│ ├── .size-limit.js
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── biome.json
│ ├── package.json
│ ├── rollup.config.js
│ ├── src
│ │ ├── components
│ │ │ ├── ColorChannelSlider.css
│ │ │ ├── ColorChannelSlider.tsx
│ │ │ ├── ColorPicker.css
│ │ │ ├── ColorPicker.tsx
│ │ │ ├── HueWheel.tsx
│ │ │ └── TrueGradient.tsx
│ │ ├── index.ts
│ │ ├── lib
│ │ │ ├── color.ts
│ │ │ ├── oklab.ts
│ │ │ ├── rgb.ts
│ │ │ └── webgl.ts
│ │ └── types.d.ts
│ ├── stylelint.config.js
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ ├── vite.config.ts
│ └── vitest.setup.ts
├── storybook
│ ├── .gitignore
│ ├── .storybook
│ │ ├── global.css
│ │ ├── main.js
│ │ └── preview.js
│ ├── LICENSE
│ ├── README.md
│ ├── biome.json
│ ├── package.json
│ ├── src
│ │ ├── Button.stories.jsx
│ │ ├── ColorChannelSlider.stories.jsx
│ │ ├── ColorPicker.stories.jsx
│ │ ├── HueWheel.stories.jsx
│ │ ├── Icon.stories.jsx
│ │ ├── Kbd.stories.jsx
│ │ ├── OmniBar.stories.jsx
│ │ ├── Select.stories.jsx
│ │ ├── Slider.stories.jsx
│ │ ├── SubtleInput.stories.jsx
│ │ ├── Switch.stories.jsx
│ │ ├── TokenType.stories.jsx
│ │ ├── TreeGrid.stories.jsx
│ │ ├── TrueGradient.stories.jsx
│ │ └── components
│ │ │ ├── StickerSheet.module.css
│ │ │ └── StickerSheet.tsx
│ ├── stylelint.config.js
│ ├── tsconfig.json
│ └── vite.config.ts
├── tiles
│ ├── .npmignore
│ ├── .size-limit.js
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── biome.json
│ ├── package.json
│ ├── rollup.config.js
│ ├── src
│ │ ├── Button
│ │ │ ├── Button.css
│ │ │ └── Button.tsx
│ │ ├── ButtonLink
│ │ │ └── ButtonLink.tsx
│ │ ├── CopyButton
│ │ │ ├── CopyButton.css
│ │ │ └── CopyButton.tsx
│ │ ├── Demo
│ │ │ ├── Demo.css
│ │ │ └── Demo.tsx
│ │ ├── Fieldset
│ │ │ ├── Fieldset.css
│ │ │ └── Fieldset.tsx
│ │ ├── Kbd
│ │ │ ├── Kbd.css
│ │ │ └── Kbd.tsx
│ │ ├── OmniBar
│ │ │ ├── OmniBar.css
│ │ │ └── OmniBar.tsx
│ │ ├── Select
│ │ │ ├── Select.css
│ │ │ └── Select.tsx
│ │ ├── Slider
│ │ │ ├── Slider.css
│ │ │ └── Slider.tsx
│ │ ├── SubtleInput
│ │ │ ├── SubtleInput.css
│ │ │ └── SubtleInput.tsx
│ │ ├── Switch
│ │ │ ├── Switch.css
│ │ │ └── Switch.tsx
│ │ ├── TokenType
│ │ │ ├── TokenType.css
│ │ │ └── TokenType.tsx
│ │ ├── Tooltip
│ │ │ ├── Tooltip.css
│ │ │ └── Tooltip.tsx
│ │ ├── TreeGrid
│ │ │ ├── Group.tsx
│ │ │ ├── Item.tsx
│ │ │ ├── Root.tsx
│ │ │ ├── TreeGrid.css
│ │ │ └── index.tsx
│ │ ├── hooks
│ │ │ └── resize-observer.ts
│ │ ├── index.ts
│ │ ├── lib
│ │ │ ├── number.test.ts
│ │ │ ├── number.ts
│ │ │ └── set.ts
│ │ └── types.d.ts
│ ├── stylelint.config.js
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── vite.config.ts
├── token-lab
│ ├── .gitignore
│ ├── .npmignore
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── biome.json
│ ├── index.html
│ ├── package.json
│ ├── public
│ │ ├── assets
│ │ │ └── terrazzo-logo.svg
│ │ └── favicon.svg
│ ├── src
│ │ ├── app.tsx
│ │ ├── components
│ │ │ ├── CodeEditor
│ │ │ │ └── CodeEditor.tsx
│ │ │ ├── CodePanel
│ │ │ │ ├── CodePanel.module.css
│ │ │ │ ├── CodePanel.module.d.css.ts
│ │ │ │ └── CodePanel.tsx
│ │ │ ├── EditableColorToken
│ │ │ │ ├── EditableColorToken.module.css
│ │ │ │ ├── EditableColorToken.module.d.css.ts
│ │ │ │ └── EditableColorToken.tsx
│ │ │ ├── EditableToken
│ │ │ │ ├── EditableToken.module.css
│ │ │ │ ├── EditableToken.module.d.css.ts
│ │ │ │ └── EditableToken.tsx
│ │ │ ├── MainNav
│ │ │ │ ├── MainNav.module.css
│ │ │ │ ├── MainNav.module.d.css.ts
│ │ │ │ └── MainNav.tsx
│ │ │ ├── TokensEditor
│ │ │ │ ├── Group.tsx
│ │ │ │ ├── TokensEditor.module.css
│ │ │ │ ├── TokensEditor.module.d.css.ts
│ │ │ │ └── TokensEditor.tsx
│ │ │ └── TokensNav
│ │ │ │ ├── TokensNav.module.css
│ │ │ │ ├── TokensNav.module.d.css.ts
│ │ │ │ └── TokensNav.tsx
│ │ ├── hooks
│ │ │ ├── navigation.ts
│ │ │ └── tokens.ts
│ │ ├── index.tsx
│ │ ├── layouts
│ │ │ └── Default
│ │ │ │ ├── Default.module.css
│ │ │ │ ├── Default.module.d.css.ts
│ │ │ │ └── Default.tsx
│ │ ├── lib
│ │ │ └── indexed-db.ts
│ │ ├── styles
│ │ │ ├── global.css
│ │ │ └── tokens.css
│ │ └── types
│ │ │ └── react.d.ts
│ ├── tsconfig.json
│ └── vite.config.ts
├── token-tools
│ ├── .npmignore
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── biome.json
│ ├── package.json
│ ├── rolldown.config.ts
│ ├── src
│ │ ├── color.ts
│ │ ├── css
│ │ │ ├── boolean.ts
│ │ │ ├── border.ts
│ │ │ ├── color.ts
│ │ │ ├── css-types.ts
│ │ │ ├── cubic-bezier.ts
│ │ │ ├── dimension.ts
│ │ │ ├── duration.ts
│ │ │ ├── font-family.ts
│ │ │ ├── font-weight.ts
│ │ │ ├── gradient.ts
│ │ │ ├── index.ts
│ │ │ ├── lib.ts
│ │ │ ├── link.ts
│ │ │ ├── number.ts
│ │ │ ├── shadow.ts
│ │ │ ├── string.ts
│ │ │ ├── stroke-style.ts
│ │ │ ├── transition.ts
│ │ │ └── typography.ts
│ │ ├── id.ts
│ │ ├── index.ts
│ │ ├── js
│ │ │ └── index.ts
│ │ ├── string.ts
│ │ ├── transform.ts
│ │ └── types.ts
│ ├── test
│ │ ├── css.test.ts
│ │ ├── id.test.ts
│ │ ├── js.test.ts
│ │ ├── string.test.ts
│ │ └── types.test.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ ├── vitest.config.ts
│ └── vitest.config.ts.timestamp-1718599329856-9fd330128a63e.mjs
├── tokens
│ ├── .gitignore
│ ├── biome.json
│ ├── dist
│ │ └── index.css
│ ├── package.json
│ ├── terrazzo.config.js
│ └── tokens.json
└── use-color
│ ├── .npmignore
│ ├── .size-limit.js
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── biome.json
│ ├── index.d.ts
│ ├── index.js
│ ├── index.test.tsx
│ ├── package.json
│ ├── tsconfig.json
│ ├── vitest.config.ts
│ └── vitest.setup.ts
├── patches
└── vite-plugin-sass-dts.patch
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── scripts
└── inject-license.js
├── stylelint.config.js
├── tsconfig.base.json
├── tsconfig.react.json
├── turbo.json
└── www
├── .gitignore
├── README.md
├── astro.config.ts
├── biome.json
├── package.json
├── public
├── assets
│ ├── apple-hig-typography.png
│ ├── cli-init.png
│ ├── colorpicker-gamut.png
│ ├── gamut.svg
│ ├── github-themes.png
│ ├── logo-figma.svg
│ ├── logo-git-butler.svg
│ ├── logo-guardian.svg
│ ├── logo-lego.svg
│ ├── logo-snyk.svg
│ ├── radix-colors.png
│ ├── social-image.png
│ ├── terrazzo-logo.svg
│ └── tooltip-dialog-light-dark-mode.png
├── favicon.svg
└── robots.txt
├── src
├── components
│ ├── SearchBox.css
│ ├── SearchBox.tsx
│ ├── ThemeSwitcher.tsx
│ ├── docs-header-nav.astro
│ ├── docs-sidenav.astro
│ ├── head-meta.astro
│ ├── illo
│ │ └── home.astro
│ ├── main-footer.astro
│ ├── main-nav.astro
│ ├── terrazzo.astro
│ └── tri.astro
├── env.d.ts
├── layouts
│ ├── default.astro
│ └── docs.astro
├── lib
│ └── navigation.ts
├── pages
│ ├── docs
│ │ ├── cli
│ │ │ ├── api
│ │ │ │ ├── js.md
│ │ │ │ └── plugin-development.md
│ │ │ ├── commands.md
│ │ │ ├── config.md
│ │ │ ├── index.md
│ │ │ ├── integrations
│ │ │ │ ├── css.md
│ │ │ │ ├── custom.md
│ │ │ │ ├── index.md
│ │ │ │ ├── js.md
│ │ │ │ ├── sass.md
│ │ │ │ ├── swift.md
│ │ │ │ └── tailwind.md
│ │ │ ├── lint.md
│ │ │ └── migrating.md
│ │ ├── components
│ │ │ ├── color-picker.mdx
│ │ │ ├── demo.mdx
│ │ │ ├── demos
│ │ │ │ ├── color-picker-basic.tsx
│ │ │ │ ├── demo-button.tsx
│ │ │ │ └── slider.tsx
│ │ │ ├── slider.mdx
│ │ │ └── tiles.md
│ │ ├── guides
│ │ │ ├── dtcg.md
│ │ │ ├── modes.md
│ │ │ └── styleguide.md
│ │ ├── index.md
│ │ ├── reference
│ │ │ ├── about.md
│ │ │ └── tokens.md
│ │ ├── token-lab
│ │ │ └── index.md
│ │ └── tokens
│ │ │ ├── index.md
│ │ │ └── modes.md
│ ├── index.astro
│ └── token-lab
│ │ └── index.astro
├── plugins
│ ├── rehype-auto-toc.js
│ └── remark-vitepress.js
├── scripts
│ ├── copy-code.js
│ ├── tabs.js
│ └── toc.js
└── styles
│ ├── app.css
│ └── docs.css
├── stylelint.config.js
└── tsconfig.json
/.changeset/README.md:
--------------------------------------------------------------------------------
1 | # Changesets
2 |
3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
4 | with multi-package repos, or single-package repos to help you version and publish your code. You can
5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets)
6 |
7 | We have a quick list of common questions to get you started engaging with this project in
8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
9 |
--------------------------------------------------------------------------------
/.changeset/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://unpkg.com/@changesets/config@3.0.2/schema.json",
3 | "changelog": [
4 | "@changesets/changelog-github",
5 | {
6 | "repo": "terrazzoapp/terrazzo"
7 | }
8 | ],
9 | "commit": false,
10 | "access": "public",
11 | "baseBranch": "main"
12 | }
13 |
--------------------------------------------------------------------------------
/.git-blame-ignore-revs:
--------------------------------------------------------------------------------
1 | # Prettier format
2 | 4b07a803ce92376ba2dff577454cf619eb53ed5e
3 | c15567f94a557cbfed32bb3bb51c7e7b98f75d49
4 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feat.yml:
--------------------------------------------------------------------------------
1 | name: Feature request
2 | description: Propose new functionality or a breaking change
3 | labels:
4 | - enhancement
5 | body:
6 | - type: textarea
7 | id: description
8 | attributes:
9 | label: Summary
10 | description: Short description of the problem, and why this could be useful.
11 | validations:
12 | required: true
13 | - type: textarea
14 | id: proposal
15 | attributes:
16 | label: Proposal & prior art
17 | description: |
18 | Describe the change in detail. Answer all the following questions: 1) What is syntax? 2) What are some alternate options, along with pros/cons of each? 3) Is this a breaking change (and if so, what is gained/lost)? 4) Any prior art to reference?
19 | validations:
20 | required: true
21 | - type: checkboxes
22 | id: extra
23 | attributes:
24 | label: Extra
25 | options:
26 | - label: I’m willing to open a PR (see [CONTRIBUTING.md](https://github.com/terrazzoapp/terrazzo/blob/main/CONTRIBUTING.md))
27 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | ## Changes
2 |
3 | _What does this PR change? Link to any related issue(s)._
4 |
5 | ## How to Review
6 |
7 | _How can a reviewer review your changes? What should be kept in mind for this review?_
8 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: release
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | changelog:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/setup-node@v4
13 | with:
14 | node-version: 22
15 | - uses: actions/checkout@v4
16 | - uses: pnpm/action-setup@v4
17 | with:
18 | run_install: true
19 | - run: pnpm run build
20 | - uses: changesets/action@v1
21 | with:
22 | version: pnpm run version
23 | publish: pnpm exec changeset publish
24 | commit: "[ci] release"
25 | title: "[ci] release"
26 | env:
27 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
28 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | dist
3 | node_modules
4 | package-lock.json
5 | .dccache
6 | .pnpm-debug*
7 | .env
8 | .turbo
9 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 22
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | *.css
2 | *.ts
3 | *.tsx
4 | *.js
5 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["biomejs.biome"]
3 | }
4 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "[css]": {
3 | "editor.defaultFormatter": "biomejs.biome"
4 | },
5 | "[astro]": {
6 | "editor.defaultFormatter": "biomejs.biome"
7 | },
8 | "[javascript]": {
9 | "editor.defaultFormatter": "biomejs.biome"
10 | },
11 | "[javascriptreact]": {
12 | "editor.defaultFormatter": "biomejs.biome"
13 | },
14 | "[json]": {
15 | "editor.defaultFormatter": "biomejs.biome"
16 | },
17 | "[markdown]": {
18 | "editor.defaultFormatter": "esbenp.prettier-vscode"
19 | },
20 | "[typescript]": {
21 | "editor.defaultFormatter": "biomejs.biome"
22 | },
23 | "[typescriptreact]": {
24 | "editor.defaultFormatter": "biomejs.biome"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Drew Powers
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ⛋ Terrazzo Monorepo
2 |
3 | This repo serves as the home for:
4 |
5 | - [Terrazzo CLI](https://terrazzo.app/docs/cli): generate code from [DTCG tokens](https://tr.designtokens.org/format/) (formerly known as “Cobalt UI”)
6 | - [CSS](https://terrazzo.app/docs/cli/integrations/css)
7 | - [Sass](https://terrazzo.app/docs/cli/integrations/sass)
8 | - [JS/TS](https://terrazzo.app/docs/cli/integrations/js)
9 | - [Swift](https://terrazzo.app/docs/cli/integrations/swift)
10 | - [Tailwind](https://terrazzo.app/docs/cli/integrations/tailwind)
11 | - [Terrazzo Color Picker](https://terrazzo.app/docs/components/color-picker), a futuristic colorpicker that can handle wide gamut and high bit-depth colors in stunning color reproduction
12 | - [Terrazzo Tiles DS](./packages/tiles/): a design system for _documenting_ design systems
13 | - Token Lab (coming soon): generate design systems from scratch, or start from an existing OSS design system
14 |
15 | ### 🔹 Cobalt UI
16 |
17 | Cobalt UI 2.0 was renamed to Terrazzo (same project, same people). To see the code for Cobalt 1.0, see the [1.x branch](https://github.com/terrazzoapp/terrazzo/tree/1.x).
18 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@terrazzo/monorepo",
3 | "private": true,
4 | "type": "module",
5 | "packageManager": "pnpm@10.11.0",
6 | "scripts": {
7 | "build": "turbo run build",
8 | "build:apps": "turbo run build:app",
9 | "changeset": "changeset",
10 | "inject-license": "node scripts/inject-license.js",
11 | "dev": "pnpm run -r --parallel --if-present dev",
12 | "lint": "turbo run lint",
13 | "format": "turbo run format",
14 | "test": "turbo run test",
15 | "prepublishOnly": "pnpm run build",
16 | "version": "pnpm run build && changeset version"
17 | },
18 | "devDependencies": {
19 | "@arethetypeswrong/cli": "^0.18.1",
20 | "@biomejs/biome": "1.9.4",
21 | "@changesets/changelog-github": "^0.5.1",
22 | "@changesets/cli": "^2.29.4",
23 | "@types/node": "^22.15.29",
24 | "execa": "^9.6.0",
25 | "prettier": "^3.5.3",
26 | "rolldown": "1.0.0-beta.10",
27 | "rolldown-plugin-dts": "^0.13.7",
28 | "strip-ansi": "^7.1.0",
29 | "stylelint": "^16.20.0",
30 | "stylelint-config-standard": "^36.0.1",
31 | "stylelint-order": "^6.0.4",
32 | "turbo": "^2.5.4",
33 | "typescript": "^5.8.3",
34 | "vitest": "^3.1.4"
35 | },
36 | "pnpm": {
37 | "patchedDependencies": {
38 | "vite-plugin-sass-dts": "patches/vite-plugin-sass-dts.patch"
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/packages/cli/.gitignore:
--------------------------------------------------------------------------------
1 | test/fixtures/**/tokens/
2 | test/fixtures/init/terrazzo.config.js
3 | test/fixtures/**/actual.json
4 |
--------------------------------------------------------------------------------
/packages/cli/.npmignore:
--------------------------------------------------------------------------------
1 | .turbo
2 | biome.json
3 | src/**
4 | test/**
5 | tsconfig.*
6 | vitest.*
7 |
--------------------------------------------------------------------------------
/packages/cli/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to @terrazzo/cli
2 |
3 | This document contains developer notes and context for @terrazzo/parser. For general contribution guidelines, see [CONTRIBUTING.md](../../CONTRIBUTING.md) in the root.
4 |
5 | ## What this package does
6 |
7 | - Runs @terrazzo/parser and saves the output to disk using Node.js
8 | - Provide watcher scripts that also do this
9 |
10 | ## What this package DOES NOT do
11 |
12 | - Parse/validate/normalize tokens.json (that’s for @terrazzo/parser)
13 | - Interact with plugins
14 |
--------------------------------------------------------------------------------
/packages/cli/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Drew Powers
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/cli/README.md:
--------------------------------------------------------------------------------
1 | # ⛋ @terrazzo/cli
2 |
3 | The Terrazzo CLI takes DTCG design tokens and generates code using plugins (e.g. [CSS](../plugin-css/), [Sass](../plugin-sass/), [JS/TS](../plugin-js/), and more). You can either run the Terrazzo CLI manually, or as part of your CI process.
4 |
5 | > [!TIP]
6 | > Migrating from Cobalt? Check out the [Migration Guide](/docs/cli/migrating)
7 |
8 | ## Quickstart
9 |
10 | First, install the package using your package manager of choice:
11 |
12 | ```sh
13 | npm i -D @terrazzo/cli
14 | ```
15 |
16 | And you can create a configuration and install plugins with the `init` command:
17 |
18 | ```sh
19 | npx tz init
20 | ```
21 |
22 | This will create a `terrazzo.config.js` starter config file, and even start your token system from a popular open source design system.
23 |
24 | [Full documentation](https://terrazzo.app/docs/cli)
25 |
--------------------------------------------------------------------------------
/packages/cli/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
3 | "extends": ["../../biome.json"],
4 | "files": {
5 | "include": ["./bin/", "./src/", "./test/", "*.tsx"]
6 | },
7 | "linter": {
8 | "rules": {
9 | "suspicious": {
10 | "noConsole": "off"
11 | }
12 | }
13 | },
14 | "overrides": [
15 | {
16 | "include": ["./bin/cli.js"],
17 | "linter": {
18 | "rules": {
19 | "suspicious": {
20 | "noConsole": "off"
21 | }
22 | }
23 | }
24 | }
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/packages/cli/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Terrazzo Token Lab
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/packages/cli/lab.tsx:
--------------------------------------------------------------------------------
1 | import TokenLab from '@terrazzo/token-lab';
2 | import React from 'react';
3 | import { createRoot } from 'react-dom/client';
4 |
5 | const response = await fetch('/api/tokens');
6 | const tokenFile = await response.text();
7 | const root = createRoot(document.getElementById('app'));
8 |
9 | root.render(
10 | {
13 | const response = await fetch('/api/tokens', {
14 | method: 'POST',
15 | headers: {
16 | 'Content-Type': 'text/plain',
17 | },
18 | body: updatedTokens,
19 | });
20 |
21 | if (!response.ok) {
22 | console.error(`Failed to save tokens: ${response.status}`);
23 | }
24 | }}
25 | />,
26 | );
27 |
--------------------------------------------------------------------------------
/packages/cli/rolldown.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'rolldown';
2 | import { dts } from 'rolldown-plugin-dts';
3 |
4 | export default defineConfig({
5 | input: {
6 | index: './src/index.ts',
7 | },
8 | plugins: [dts()],
9 | external: [
10 | '@clack/prompts',
11 | '@hono/node-server',
12 | '@humanwhocodes/momoa',
13 | '@terrazzo/parser',
14 | '@terrazzo/token-lab',
15 | '@terrazzo/token-tools',
16 | '@types/escodegen',
17 | 'chokidar',
18 | 'detect-package-manager',
19 | 'dotenv',
20 | 'escodegen',
21 | 'merge-anything',
22 | 'meriyah',
23 | 'mime',
24 | 'picocolors',
25 | 'yaml-to-momoa',
26 | ],
27 | output: {
28 | dir: 'dist',
29 | format: 'es',
30 | sourcemap: true,
31 | },
32 | });
33 |
--------------------------------------------------------------------------------
/packages/cli/src/check.ts:
--------------------------------------------------------------------------------
1 | import { type ConfigInit, type Logger, parse } from '@terrazzo/parser';
2 | import yamlToMomoa from 'yaml-to-momoa';
3 | import { loadTokens, printError, printSuccess, resolveTokenPath } from './shared.js';
4 |
5 | export interface CheckOptions {
6 | /** positional CLI args */
7 | positionals: string[];
8 | config: ConfigInit;
9 | logger: Logger;
10 | }
11 |
12 | /** tz check */
13 | export async function checkCmd({ config, logger, positionals }: CheckOptions) {
14 | try {
15 | const startTime = performance.now();
16 | const tokenPaths = positionals.slice(1).length
17 | ? positionals.slice(1).map((tokenPath) => resolveTokenPath(tokenPath, { logger }))
18 | : config.tokens;
19 | const sources = await loadTokens(tokenPaths, { logger });
20 | if (!sources?.length) {
21 | logger.error({ group: 'config', message: 'Couldn’t find any tokens. Run `npx tz init` to create some.' });
22 | return;
23 | }
24 | await parse(sources, { config, continueOnError: true, logger, yamlToMomoa }); // will throw if errors
25 | printSuccess('No errors', startTime);
26 | } catch (err) {
27 | printError((err as Error).message);
28 | process.exit(1);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/packages/cli/src/help.ts:
--------------------------------------------------------------------------------
1 | /** Show help */
2 | export function helpCmd() {
3 | console.log(`tz
4 | [commands]
5 | build Build token artifacts from tokens.json
6 | --watch, -w Watch tokens.json for changes and recompile
7 | --no-lint Disable linters running on build
8 | check [path] Check tokens.json for errors and run linters
9 | lint [path] (alias of check)
10 | init Create a starter tokens.json file
11 | lab Manage your tokens with a web interface
12 |
13 | [options]
14 | --help Show this message
15 | --config, -c Path to config (default: ./terrazzo.config.js)
16 | --quiet Suppress warnings
17 | `);
18 | }
19 |
--------------------------------------------------------------------------------
/packages/cli/src/version.ts:
--------------------------------------------------------------------------------
1 | import fs from 'node:fs';
2 |
3 | export function versionCmd() {
4 | const { version } = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
5 | console.log(version);
6 | }
7 |
--------------------------------------------------------------------------------
/packages/cli/terrazzo.config.mjs:
--------------------------------------------------------------------------------
1 | import { defineConfig } from '@terrazzo/cli';
2 |
3 | export default defineConfig({
4 | tokens: ['tokens-example.json'],
5 | outDir: './tokens/',
6 | plugins: [
7 | /** @see https://terrazzo.app/docs/cli/integrations */
8 | ],
9 | lint: {
10 | /** @see https://terrazzo.app/docs/cli/lint */
11 | },
12 | });
13 |
--------------------------------------------------------------------------------
/packages/cli/test/fixtures/check-config/styles/tokens.json:
--------------------------------------------------------------------------------
1 | {
2 | "color": {
3 | "blue": {
4 | "100": {
5 | "$type": "color",
6 | "$value": { "colorSpace": "srgb", "components": [0, 0.2, 1], "alpha": 1 }
7 | }
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/packages/cli/test/fixtures/check-config/terrazzo.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | tokens: ['./styles/tokens.json'],
3 | };
4 |
--------------------------------------------------------------------------------
/packages/cli/test/fixtures/check-invalid/tokens.json:
--------------------------------------------------------------------------------
1 | {
2 | "color": {
3 | "blue": {
4 | "100": {
5 | "$type": "color",
6 | "$value": { "colorSpace": "srgb", "components": "[0, 0.2, 1]", "alpha": 1 }
7 | }
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/packages/cli/test/fixtures/check-npm/.gitignore:
--------------------------------------------------------------------------------
1 | # `npm i` needed for this test for Node resolution.
2 | # No actual node_modules were harmed in the making of this test.
3 | !node_modules
4 | !package-lock.json
5 |
--------------------------------------------------------------------------------
/packages/cli/test/fixtures/check-npm/node_modules/.package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@terrazzo/test-cli",
3 | "lockfileVersion": 3,
4 | "requires": true,
5 | "packages": {
6 | "node_modules/my-other-tokens": {
7 | "resolved": "pkg-exports",
8 | "link": true
9 | },
10 | "node_modules/my-tokens": {
11 | "resolved": "pkg",
12 | "link": true
13 | },
14 | "packages/pkg": {
15 | "extraneous": true
16 | },
17 | "pkg": {
18 | "name": "my-tokens",
19 | "dev": true
20 | },
21 | "pkg-exports": {
22 | "name": "my-other-tokens",
23 | "dev": true
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/packages/cli/test/fixtures/check-npm/node_modules/my-other-tokens:
--------------------------------------------------------------------------------
1 | ../pkg-exports
--------------------------------------------------------------------------------
/packages/cli/test/fixtures/check-npm/node_modules/my-tokens:
--------------------------------------------------------------------------------
1 | ../pkg
--------------------------------------------------------------------------------
/packages/cli/test/fixtures/check-npm/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@terrazzo/test-cli",
3 | "lockfileVersion": 3,
4 | "requires": true,
5 | "packages": {
6 | "": {
7 | "name": "@terrazzo/test-cli",
8 | "devDependencies": {
9 | "my-other-tokens": "file:./pkg-exports",
10 | "my-tokens": "file:./pkg"
11 | }
12 | },
13 | "node_modules/my-other-tokens": {
14 | "resolved": "pkg-exports",
15 | "link": true
16 | },
17 | "node_modules/my-tokens": {
18 | "resolved": "pkg",
19 | "link": true
20 | },
21 | "packages/pkg": {
22 | "extraneous": true
23 | },
24 | "pkg": {
25 | "name": "my-tokens",
26 | "dev": true
27 | },
28 | "pkg-exports": {
29 | "name": "my-other-tokens",
30 | "dev": true
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/packages/cli/test/fixtures/check-npm/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@terrazzo/test-cli",
3 | "private": true,
4 | "type": "module",
5 | "devDependencies": {
6 | "my-tokens": "file:./pkg",
7 | "my-other-tokens": "file:./pkg-exports"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/packages/cli/test/fixtures/check-npm/pkg-exports/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "my-other-tokens",
3 | "private": true,
4 | "type": "module",
5 | "exports": {
6 | "./aliased": "./tokens-xyz.json"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/cli/test/fixtures/check-npm/pkg-exports/tokens-xyz.json:
--------------------------------------------------------------------------------
1 | {
2 | "color": {
3 | "blue": {
4 | "$type": "color",
5 | "4": {
6 | "$value": { "colorSpace": "oklch", "components": [0.9381, 0.035, 234.8] }
7 | }
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/packages/cli/test/fixtures/check-npm/pkg/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "my-tokens",
3 | "private": true,
4 | "type": "module"
5 | }
6 |
--------------------------------------------------------------------------------
/packages/cli/test/fixtures/check-npm/pkg/tokens.json:
--------------------------------------------------------------------------------
1 | {
2 | "color": {
3 | "blue": {
4 | "$type": "color",
5 | "4": {
6 | "$value": { "colorSpace": "oklch", "components": [0.9381, 0.035, 234.8] }
7 | }
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/packages/cli/test/fixtures/check-npm/terrazzo.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from '../../../dist/index.js';
2 |
3 | export default defineConfig({
4 | tokens: ['my-tokens/tokens.json', 'my-other-tokens/aliased'],
5 | });
6 |
--------------------------------------------------------------------------------
/packages/cli/test/fixtures/check-valid/tokens.json:
--------------------------------------------------------------------------------
1 | {
2 | "color": {
3 | "blue": {
4 | "100": {
5 | "$type": "color",
6 | "$value": { "colorSpace": "srgb", "components": [0, 0.2, 1], "alpha": 1 }
7 | }
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/packages/cli/test/fixtures/error-no-default-export/terrazzo.config.js:
--------------------------------------------------------------------------------
1 | export const config = {};
2 |
--------------------------------------------------------------------------------
/packages/cli/test/fixtures/error-no-tokens/terrazzo.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from '../../../dist/index.js';
2 |
3 | export default defineConfig({});
4 |
--------------------------------------------------------------------------------
/packages/cli/test/normalize.test.ts:
--------------------------------------------------------------------------------
1 | import fs from 'node:fs';
2 | import { fileURLToPath } from 'node:url';
3 | import { execa } from 'execa';
4 | import { describe, expect, test } from 'vitest';
5 |
6 | const cmd = '../../../bin/cli.js';
7 |
8 | describe('tz normalize', () => {
9 | test('basic', async () => {
10 | const cwd = new URL('./fixtures/normalize/', import.meta.url);
11 | await execa(cmd, ['normalize', 'input.json', '--output', 'actual.json'], { cwd });
12 | await expect(fs.readFileSync(new URL('./actual.json', cwd), 'utf8')).toMatchFileSnapshot(
13 | fileURLToPath(new URL('./want.json', cwd)),
14 | );
15 | });
16 |
17 | test('basic (shortcut)', async () => {
18 | const cwd = new URL('./fixtures/normalize/', import.meta.url);
19 | await execa(cmd, ['normalize', 'input.json', '-o', 'actual.json'], { cwd });
20 | await expect(fs.readFileSync(new URL('./actual.json', cwd), 'utf8')).toMatchFileSnapshot(
21 | fileURLToPath(new URL('./want.json', cwd)),
22 | );
23 | });
24 |
25 | test('missing input', async () => {
26 | const cwd = new URL('./fixtures/normalize/', import.meta.url);
27 | await expect(execa(cmd, ['normalize', '--output', 'actual.json'], { cwd })).rejects.toThrow(
28 | `Command failed with exit code 1: ../../../bin/cli.js normalize --output actual.json
29 |
30 | ✗ [config] Expected input: \`tz normalize -o output.json\``,
31 | );
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/packages/cli/test/version.test.ts:
--------------------------------------------------------------------------------
1 | import { execa } from 'execa';
2 | import { describe, expect, test } from 'vitest';
3 |
4 | const cmd = './bin/cli.js';
5 |
6 | describe('tz --version', () => {
7 | test('returns version', async () => {
8 | const { stdout } = await execa('node', [cmd, '--version']);
9 | expect(stdout).toMatch(/^\d+\.\d+\.\d+/);
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/packages/cli/tokens-example.json:
--------------------------------------------------------------------------------
1 | {
2 | "color": {
3 | "$description": "Color tokens",
4 | "$type": "color",
5 | "black": {
6 | "100": { "$value": "#0c0c0d0d" },
7 | "200": { "$value": "#0c0c0d1a" },
8 | "300": { "$value": "#0c0c0d33" },
9 | "400": { "$value": "#0c0c0456" },
10 | "500": { "$value": "#0c0c0db2" },
11 | "600": { "$value": "#0c0c0dcc" },
12 | "700": { "$value": "#0c0c0dd9" },
13 | "800": { "$value": "#0c0c0de5" },
14 | "900": { "$value": "#0c0c0df2" },
15 | "1000": { "$value": "#0c0c0d" }
16 | }
17 | },
18 | "border": {
19 | "$description": "Border tokens",
20 | "$type": "border"
21 | },
22 | "space": {
23 | "$description": "Dimension tokens",
24 | "$type": "dimension"
25 | },
26 | "typography": {
27 | "$description": "Typography tokens",
28 | "$type": "typography"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/packages/cli/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "baseUrl": "src",
5 | "outDir": "dist"
6 | },
7 | "include": ["./src/", "./test/"]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/cli/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react-swc';
3 |
4 | export default defineConfig({
5 | plugins: [react()],
6 | build: {
7 | outDir: 'dist/lab',
8 | rollupOptions: {
9 | external: ['@terrazzo/tiles', '@terrazzo/token-lab'],
10 | },
11 | emptyOutDir: true,
12 | sourcemap: true,
13 | target: 'es2024',
14 | },
15 | });
16 |
--------------------------------------------------------------------------------
/packages/cli/vitest.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vitest/config';
2 |
3 | export default defineConfig({
4 | test: {
5 | testTimeout: 10_000,
6 | },
7 | });
8 |
--------------------------------------------------------------------------------
/packages/fonts/FragmentMono-Italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terrazzoapp/terrazzo/ac939ed496a483874e2fc6ffb949cc70f647593f/packages/fonts/FragmentMono-Italic.woff2
--------------------------------------------------------------------------------
/packages/fonts/FragmentMono-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terrazzoapp/terrazzo/ac939ed496a483874e2fc6ffb949cc70f647593f/packages/fonts/FragmentMono-Regular.woff2
--------------------------------------------------------------------------------
/packages/fonts/InstrumentSans-Italic-VF.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terrazzoapp/terrazzo/ac939ed496a483874e2fc6ffb949cc70f647593f/packages/fonts/InstrumentSans-Italic-VF.woff2
--------------------------------------------------------------------------------
/packages/fonts/InstrumentSans-VF.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terrazzoapp/terrazzo/ac939ed496a483874e2fc6ffb949cc70f647593f/packages/fonts/InstrumentSans-VF.woff2
--------------------------------------------------------------------------------
/packages/fonts/README.md:
--------------------------------------------------------------------------------
1 | # Terrazzo Fonts
2 |
3 | Redistributions of SIL open source fonts. All rights reserved by the respective license owners. Not affiliated with this project in any way.
4 |
5 | - [Instrument Sans](https://github.com/Instrument/instrument-sans)
6 | - [Fragment Mono](https://github.com/weiweihuanghuang/fragment-mono).
7 |
8 | ## Setup
9 |
10 | ```sh
11 | pnpm i @terrazzo/fonts
12 | ```
13 |
14 | In any Vite-powered setup, import CSS in any JS or TS entry file, and the fonts should be loaded automatically.
15 |
16 | ```ts
17 | import '@terrazzo/fonts/fragment-mono.css';
18 | import '@terrazzo/fonts/instrument-sans.css';
19 | ```
20 |
--------------------------------------------------------------------------------
/packages/fonts/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
3 | "extends": ["../../biome.json"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/fonts/fragment-mono.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Fragment Mono';
3 | font-style: normal;
4 | src: url('./FragmentMono-Regular.woff2') format('woff2');
5 | }
6 |
7 | @font-face {
8 | font-family: 'Fragment Mono';
9 | font-style: italic;
10 | src: url('./FragmentMono-Italic.woff2') format('woff2');
11 | }
12 |
--------------------------------------------------------------------------------
/packages/fonts/instrument-sans.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Instrument Sans';
3 | font-style: normal;
4 | src: url('./InstrumentSans-VF.woff2') format('woff2');
5 | }
6 |
7 | @font-face {
8 | font-family: 'Instrument Sans';
9 | font-style: italic;
10 | src: url('./InstrumentSans-Italic-VF.woff2') format('woff2');
11 | }
12 |
--------------------------------------------------------------------------------
/packages/fonts/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@terrazzo/fonts",
3 | "version": "0.1.0",
4 | "type": "module",
5 | "license": "OFL-1.1",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://github.com/terrazzoapp/terrazzo.git",
9 | "directory": "./packages/fonts/"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/icons/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 |
--------------------------------------------------------------------------------
/packages/icons/.npmignore:
--------------------------------------------------------------------------------
1 | .turbo
2 | biome.json
3 | src/**
4 | rollup.*
5 | tsconfig.*
6 | vite.*
7 | vitest.*
8 |
--------------------------------------------------------------------------------
/packages/icons/README.md:
--------------------------------------------------------------------------------
1 | # @terrazzo/icons
2 |
3 | Icon set for Terrazzo. Repackaged icons from ['react-icons'](https://react-icons.github.io/react-icons/) because that project isn’t maintained anymore.
4 |
--------------------------------------------------------------------------------
/packages/icons/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
3 | "extends": ["../../biome.json"],
4 | "linter": {
5 | "rules": {
6 | "a11y": {
7 | "noInteractiveElementToNoninteractiveRole": "off"
8 | }
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/icons/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@terrazzo/icons",
3 | "version": "0.1.0",
4 | "type": "module",
5 | "author": {
6 | "name": "Drew Powers",
7 | "email": "drew@pow.rs"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/terrazzoapp/terrazzo.git",
12 | "directory": "./packages/icons/"
13 | },
14 | "main": "./dist/index.js",
15 | "scripts": {
16 | "build": "rollup -c rollup.config.js",
17 | "format": "biome check --fix --unsafe src",
18 | "lint": "pnpm --filter @terrazzo/icons run \"/^lint:(js|ts)/\"",
19 | "lint:js": "biome check .",
20 | "lint:ts": "tsc --noEmit"
21 | },
22 | "peerDependencies": {
23 | "react": "^19.0.0",
24 | "react-dom": "^19.0.0"
25 | },
26 | "devDependencies": {
27 | "@rollup/plugin-typescript": "^12.1.2",
28 | "@types/react": "^19.1.1",
29 | "@types/react-dom": "^19.1.2",
30 | "react": "19.0.0",
31 | "react-dom": "19.0.0",
32 | "rollup": "^4.40.0",
33 | "rollup-plugin-import-css": "^3.5.8"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/packages/icons/rollup.config.js:
--------------------------------------------------------------------------------
1 | import ts from '@rollup/plugin-typescript';
2 |
3 | /** @type {import("rollup").InputOptions} */
4 | export default {
5 | plugins: [ts()],
6 | input: 'src/index.tsx',
7 | external: ['*'],
8 | output: {
9 | dir: './dist/',
10 | sourcemap: true,
11 | globals: {
12 | 'react/jsx-runtime': 'jsxRuntime',
13 | 'react-dom/client': 'ReactDOM',
14 | react: 'React',
15 | },
16 | },
17 | };
18 |
--------------------------------------------------------------------------------
/packages/icons/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.react.json",
3 | "compilerOptions": {
4 | "baseUrl": "src",
5 | "module": "nodenext",
6 | "outDir": "dist"
7 | },
8 | "include": ["./src/"]
9 | }
10 |
--------------------------------------------------------------------------------
/packages/parser/.npmignore:
--------------------------------------------------------------------------------
1 | .turbo
2 | biome.json
3 | test/
4 | tsconfig.*
5 | vitest.*
6 |
--------------------------------------------------------------------------------
/packages/parser/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to @terrazzo/parser
2 |
3 | This document contains developer notes and context for @terrazzo/parser. For general contribution guidelines, see [CONTRIBUTING.md](../../CONTRIBUTING.md) in the root.
4 |
5 | ## What this package does
6 |
7 | - Parses, validates, and normalizes tokens.json
8 | - Executes plugins and builds output
9 | - Executes linting
10 |
11 | ## What this package DOES NOT do
12 |
13 | - Write files to disk (that’s a job for Node.js; this can be run in a browser)
14 |
--------------------------------------------------------------------------------
/packages/parser/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Drew Powers
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/parser/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
3 | "extends": ["../../biome.json"],
4 | "files": {
5 | "include": ["./src/", "./test/"]
6 | },
7 | "linter": {
8 | "rules": {
9 | "suspicious": {
10 | "noExplicitAny": "off"
11 | }
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/packages/parser/rolldown.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'rolldown';
2 | import { dts } from 'rolldown-plugin-dts';
3 |
4 | export default defineConfig({
5 | input: {
6 | index: './src/index.ts',
7 | },
8 | plugins: [dts()],
9 | external: [
10 | '@humanwhocodes/mamoa',
11 | '@terrazzo/token-tools',
12 | 'culori',
13 | 'merge-anything',
14 | 'picocolors',
15 | 'wildcard-match',
16 | 'yaml-to-momoa',
17 | 'yaml',
18 | ],
19 | output: {
20 | dir: 'dist',
21 | format: 'es',
22 | sourcemap: true,
23 | },
24 | });
25 |
--------------------------------------------------------------------------------
/packages/parser/src/lint/plugin-core/lib/docs.ts:
--------------------------------------------------------------------------------
1 | export function docsLink(ruleName: string): string {
2 | return `https://terrazzo.app/docs/cli/lint#${ruleName.replaceAll('/', '')}`;
3 | }
4 |
--------------------------------------------------------------------------------
/packages/parser/src/lint/plugin-core/rules/descriptions.ts:
--------------------------------------------------------------------------------
1 | import { isTokenMatch } from '@terrazzo/token-tools';
2 | import type { LintRule } from '../../../types.js';
3 | import { docsLink } from '../lib/docs.js';
4 |
5 | export const DESCRIPTIONS = 'core/descriptions';
6 |
7 | export interface RuleDescriptionsOptions {
8 | /** Token IDs to ignore. Supports globs (`*`). */
9 | ignore?: string[];
10 | }
11 |
12 | const ERROR_MISSING_DESCRIPTION = 'MISSING_DESCRIPTION';
13 |
14 | const rule: LintRule = {
15 | meta: {
16 | messages: {
17 | [ERROR_MISSING_DESCRIPTION]: '{{ id }} missing description',
18 | },
19 | docs: {
20 | description: 'Enforce tokens have descriptions.',
21 | url: docsLink(DESCRIPTIONS),
22 | },
23 | },
24 | defaultOptions: {},
25 | create({ tokens, options, report }) {
26 | for (const t of Object.values(tokens)) {
27 | if (options.ignore && isTokenMatch(t.id, options.ignore)) {
28 | continue;
29 | }
30 | if (!t.$description) {
31 | report({
32 | messageId: ERROR_MISSING_DESCRIPTION,
33 | data: { id: t.id },
34 | node: t.source.node,
35 | });
36 | }
37 | }
38 | },
39 | };
40 |
41 | export default rule;
42 |
--------------------------------------------------------------------------------
/packages/parser/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "baseUrl": "src",
5 | "noEmit": true
6 | },
7 | "include": ["./src/", "./test/"],
8 | "exclude": ["node_modules"]
9 | }
10 |
--------------------------------------------------------------------------------
/packages/parser/vitest.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vitest/config';
2 |
3 | export default defineConfig({
4 | test: {
5 | restoreMocks: true,
6 | },
7 | });
8 |
--------------------------------------------------------------------------------
/packages/plugin-css/.gitignore:
--------------------------------------------------------------------------------
1 | test/fixtures/**/tokens/
2 |
--------------------------------------------------------------------------------
/packages/plugin-css/.npmignore:
--------------------------------------------------------------------------------
1 | .turbo
2 | biome.json
3 | src/**
4 | test/**
5 | tsconfig.*
6 | vitest.*
7 |
--------------------------------------------------------------------------------
/packages/plugin-css/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Drew Powers
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/plugin-css/README.md:
--------------------------------------------------------------------------------
1 | # ⛋ @terrazzo/plugin-css
2 |
3 | Convert DTCG tokens into CSS variables for use in any web application or native app with webview. Convert your modes into CSS media queries for complete flexibility.
4 |
5 | ## Setup
6 |
7 | Requires [Node.js 20 or later](https://nodejs.org) and [the CLI installed](https://terrazzo.app/docs/cli). With both installed, run:
8 |
9 | ```sh
10 | npm i -D @terrazzo/plugin-css
11 | ```
12 |
13 | Add a `terrazzo.config.js` to the root of your project:
14 |
15 | ```ts
16 | import { defineConfig } from "@terrazzo/cli";
17 | import css from "@terrazzo/plugin-css";
18 |
19 | export default defineConfig({
20 | outDir: "./tokens/",
21 | plugins: [
22 | css({
23 | filename: "tokens.css",
24 | }),
25 | ],
26 | });
27 | ```
28 |
29 | Lastly, run:
30 |
31 | ```sh
32 | npx tz build
33 | ```
34 |
35 | And you’ll see a `./tokens/tokens.css` file generated in your project.
36 |
37 | [Full Documentation](https://terrazzo.app/docs/integrations/css)
38 |
--------------------------------------------------------------------------------
/packages/plugin-css/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
3 | "extends": ["../../biome.json"],
4 | "files": {
5 | "include": ["./src/", "./test/"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/plugin-css/rolldown.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'rolldown';
2 | import { dts } from 'rolldown-plugin-dts';
3 |
4 | export default defineConfig({
5 | input: {
6 | index: './src/index.ts',
7 | },
8 | plugins: [dts()],
9 | output: {
10 | dir: 'dist',
11 | format: 'es',
12 | sourcemap: true,
13 | },
14 | });
15 |
--------------------------------------------------------------------------------
/packages/plugin-css/test/fixtures/base-selector/tokens.json:
--------------------------------------------------------------------------------
1 | {
2 | "foo": {
3 | "$type": "color",
4 | "$value": { "colorSpace": "oklch", "components": [0.6412, 0.239, 254.98] }
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/packages/plugin-css/test/fixtures/base-selector/want.css:
--------------------------------------------------------------------------------
1 | /* -------------------------------------------
2 | * Autogenerated by ⛋ Terrazzo. DO NOT EDIT!
3 | * ------------------------------------------- */
4 |
5 | :host {
6 | --foo: oklch(0.6333612529106073 0.20110650544801523 253.921996151641);
7 | }
8 |
9 | @media (color-gamut: p3) {
10 | :host {
11 | --foo: oklch(0.6312831744667307 0.21843061935804214 253.28399755832424);
12 | }
13 | }
14 |
15 | @media (color-gamut: rec2020) {
16 | :host {
17 | --foo: oklch(0.6336836232352946 0.2186805283935796 254.20132843068833);
18 | }
19 | }
20 |
21 |
--------------------------------------------------------------------------------
/packages/plugin-css/test/fixtures/build-default/terrazzo.config.js:
--------------------------------------------------------------------------------
1 | import pluginCSS from '../../../../plugin-css/dist/index.js';
2 | import { defineConfig } from '../../../dist/index.js';
3 |
4 | export default defineConfig({
5 | plugins: [
6 | pluginCSS({
7 | modeSelectors: [
8 | { mode: 'Light', selectors: ['@media (prefers-color-scheme: light)'] },
9 | { mode: 'Dark', selectors: ['@media (prefers-color-scheme: dark)'] },
10 | ],
11 | }),
12 | ],
13 | });
14 |
--------------------------------------------------------------------------------
/packages/plugin-css/test/fixtures/chained-selector/tokens.json:
--------------------------------------------------------------------------------
1 | {
2 | "color": {
3 | "$type": "color",
4 | "blue": {
5 | "6": {
6 | "$value": "#0550ae",
7 | "$extensions": {
8 | "mode": {
9 | "light": "#0550ae",
10 | "light-colorblind": "#0550ae",
11 | "light-high-contrast": "#023b95",
12 | "dark": "#1158c7",
13 | "dark-dimmed": "#255ab2",
14 | "dark-high-contrast": "#318bf8",
15 | "dark-colorblind": "#1158c7"
16 | }
17 | }
18 | }
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/packages/plugin-css/test/fixtures/cli-config-outdir/.gitignore:
--------------------------------------------------------------------------------
1 | styles/out/actual.css
2 |
--------------------------------------------------------------------------------
/packages/plugin-css/test/fixtures/cli-config-outdir/styles/out/want.css:
--------------------------------------------------------------------------------
1 | /* -------------------------------------------
2 | * Autogenerated by ⛋ Terrazzo. DO NOT EDIT!
3 | * ------------------------------------------- */
4 |
5 | :root {
6 | --color-base-gray-10: lab(0.1 0 0);
7 | }
8 |
9 |
--------------------------------------------------------------------------------
/packages/plugin-css/test/fixtures/cli-config-outdir/styles/tokens.json:
--------------------------------------------------------------------------------
1 | {
2 | "color": {
3 | "$type": "color",
4 | "base": {
5 | "gray": {
6 | "10": { "$value": { "colorSpace": "lab", "components": [0.1, 0, 0], "alpha": 1 } }
7 | }
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/packages/plugin-css/test/fixtures/cli-config-outdir/terrazzo.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from '@terrazzo/cli';
2 | import css from '../../../dist/index.js';
3 |
4 | export default defineConfig({
5 | tokens: ['./styles/tokens.json'],
6 | outDir: './styles/',
7 | plugins: [
8 | css({
9 | filename: 'out/actual.css',
10 | }),
11 | ],
12 | });
13 |
--------------------------------------------------------------------------------
/packages/plugin-css/test/fixtures/cli-skip-build/terrazzo.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from '@terrazzo/cli';
2 | import css from '../../../dist/index.js';
3 |
4 | export default defineConfig({
5 | tokens: ['./tokens.json'],
6 | plugins: [
7 | css({
8 | skipBuild: true,
9 | }),
10 | ],
11 | });
12 |
--------------------------------------------------------------------------------
/packages/plugin-css/test/fixtures/cli-skip-build/tokens.json:
--------------------------------------------------------------------------------
1 | {
2 | "color": {
3 | "$type": "color",
4 | "base": {
5 | "gray": {
6 | "10": { "$value": { "colorSpace": "lab", "components": [0.1, 0, 0], "alpha": 1 } }
7 | }
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/packages/plugin-css/test/fixtures/cli-watch/terrazzo.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from '@terrazzo/cli';
2 | import pluginCSS from '../../../dist/index.js';
3 |
4 | export default defineConfig({
5 | plugins: [
6 | pluginCSS({
7 | modeSelectors: [
8 | { mode: 'Light', selectors: ['@media (prefers-color-scheme: light)'] },
9 | { mode: 'Dark', selectors: ['@media (prefers-color-scheme: dark)'] },
10 | ],
11 | }),
12 | ],
13 | });
14 |
--------------------------------------------------------------------------------
/packages/plugin-css/test/fixtures/cli-yaml/terrazzo.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from '@terrazzo/cli';
2 | import pluginCSS from '../../../dist/index.js';
3 |
4 | export default defineConfig({
5 | // expect Terrazzo to automatically pick up on "tokens.yaml" filename
6 | plugins: [
7 | pluginCSS({
8 | modeSelectors: [
9 | { mode: 'Light', selectors: ['@media (prefers-color-scheme: light)'] },
10 | { mode: 'Dark', selectors: ['@media (prefers-color-scheme: dark)'] },
11 | ],
12 | }),
13 | ],
14 | });
15 |
--------------------------------------------------------------------------------
/packages/plugin-css/test/fixtures/cli/terrazzo.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from '@terrazzo/cli';
2 | import pluginCSS from '../../../dist/index.js';
3 |
4 | export default defineConfig({
5 | plugins: [
6 | pluginCSS({
7 | modeSelectors: [
8 | { mode: 'Light', selectors: ['@media (prefers-color-scheme: light)'] },
9 | { mode: 'Dark', selectors: ['@media (prefers-color-scheme: dark)'] },
10 | ],
11 | }),
12 | ],
13 | });
14 |
--------------------------------------------------------------------------------
/packages/plugin-css/test/fixtures/type-boolean/tokens.json:
--------------------------------------------------------------------------------
1 | {
2 | "bool": {
3 | "uh-huh": {
4 | "$type": "boolean",
5 | "$value": true
6 | },
7 | "nuh-uh": {
8 | "$type": "boolean",
9 | "$value": false
10 | },
11 | "idk": {
12 | "$value": "{bool.nuh-uh}"
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/packages/plugin-css/test/fixtures/type-boolean/want.css:
--------------------------------------------------------------------------------
1 | /* -------------------------------------------
2 | * Autogenerated by ⛋ Terrazzo. DO NOT EDIT!
3 | * ------------------------------------------- */
4 |
5 | :root {
6 | --ds-bool-idk: var(--ds-bool-nuh-uh);
7 | --ds-bool-nuh-uh: 0;
8 | --ds-bool-uh-huh: 1;
9 | }
10 |
11 |
--------------------------------------------------------------------------------
/packages/plugin-css/test/fixtures/type-dimension/want.css:
--------------------------------------------------------------------------------
1 | /* -------------------------------------------
2 | * Autogenerated by ⛋ Terrazzo. DO NOT EDIT!
3 | * ------------------------------------------- */
4 |
5 | :root {
6 | --ds-control-default-size: var(--ds-space-600);
7 | --ds-control-small-size: var(--ds-space-500);
8 | --ds-space-000: 0.125rem;
9 | --ds-space-2x-large: 3rem;
10 | --ds-space-3x-large: 4rem;
11 | --ds-space-100: 0.25rem;
12 | --ds-space-200: 0.5rem;
13 | --ds-space-300: 0.75rem;
14 | --ds-space-400: 1rem;
15 | --ds-space-500: 1.5rem;
16 | --ds-space-600: 2rem;
17 | --ds-space-700: 2.5rem;
18 | --ds-space-800: 3rem;
19 | --ds-space-900: 4rem;
20 | --ds-space-large: 1.5rem;
21 | --ds-space-x-large: 2rem;
22 | }
23 |
24 | @media (width >= 600px) {
25 | :root {
26 | --ds-control-default-size: var(--ds-space-600);
27 | --ds-control-small-size: var(--ds-space-500);
28 | --ds-space-000: 0.25rem;
29 | --ds-space-100: 0.375rem;
30 | --ds-space-200: 0.75rem;
31 | --ds-space-300: 1.25rem;
32 | --ds-space-400: 1.25rem;
33 | --ds-space-500: 1.75rem;
34 | --ds-space-600: 2.5rem;
35 | --ds-space-700: 3.5rem;
36 | --ds-space-800: 4rem;
37 | --ds-space-900: 6rem;
38 | }
39 | }
40 |
41 |
--------------------------------------------------------------------------------
/packages/plugin-css/test/fixtures/type-gradient/tokens.json:
--------------------------------------------------------------------------------
1 | {
2 | "gradient": {
3 | "$type": "gradient",
4 | "blue-to-green": {
5 | "$value": [
6 | { "color": { "colorSpace": "oklch", "components": [0.4559, 0.328, 265.76], "hex": "#051fcf" }, "position": 0 },
7 | { "color": { "colorSpace": "oklch", "components": [0.8206, 0.286, 156.71], "hex": "#00e089" }, "position": 1 }
8 | ],
9 | "$extensions": {
10 | "mode": {
11 | "light": [
12 | {
13 | "color": { "colorSpace": "oklch", "components": [0.4559, 0.328, 265.76], "hex": "#051fcf" },
14 | "position": 0
15 | },
16 | {
17 | "color": { "colorSpace": "oklch", "components": [0.8206, 0.286, 156.71], "hex": "#00e089" },
18 | "position": 1
19 | }
20 | ]
21 | }
22 | }
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/plugin-css/test/fixtures/type-shadow/want.css:
--------------------------------------------------------------------------------
1 | /* -------------------------------------------
2 | * Autogenerated by ⛋ Terrazzo. DO NOT EDIT!
3 | * ------------------------------------------- */
4 |
5 | :root {
6 | --ds-shadow-base: var(--ds-shadow-simple);
7 | --ds-shadow-inset: inset 0 4px 8px 0 color(srgb 0 0 0 / 0.15);
8 | --ds-shadow-layered: 0 1px 1px 0 color(srgb 0 0 0 / 0.12), 0 2px 2px 0 color(srgb 0 0 0 / 0.12), 0 4px 4px 0 color(srgb 0 0 0 / 0.12), 0 8px 8px 0 color(srgb 0 0 0 / 0.12), 0 16px 16px 0 color(srgb 0 0 0 / 0.12);
9 | --ds-shadow-neon: 0 0 0 0.5rem oklch(0.7856631286711945 0.2500998352524085 144.59678318808196);
10 | --ds-shadow-simple: 0 4px 8px 0 color(srgb 0 0 0 / 0.15);
11 | }
12 |
13 | @media (color-gamut: p3) {
14 | :root {
15 | --ds-shadow-neon: 0 0 0 0.5rem oklch(0.7831365401896191 0.34000664092427674 145.64495037017775);
16 | }
17 | }
18 |
19 | @media (color-gamut: rec2020) {
20 | :root {
21 | --ds-shadow-neon: 0 0 0 0.5rem oklch(0.7765 0.3530000000000003 147.18);
22 | }
23 | }
24 |
25 |
--------------------------------------------------------------------------------
/packages/plugin-css/test/fixtures/type-string/tokens.json:
--------------------------------------------------------------------------------
1 | {
2 | "string": {
3 | "test": {
4 | "$type": "string",
5 | "$value": "test"
6 | },
7 | "test-2": {
8 | "$value": "{string.test}"
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/plugin-css/test/fixtures/type-string/want.css:
--------------------------------------------------------------------------------
1 | /* -------------------------------------------
2 | * Autogenerated by ⛋ Terrazzo. DO NOT EDIT!
3 | * ------------------------------------------- */
4 |
5 | :root {
6 | --ds-string-test: test;
7 | --ds-string-test-2: var(--ds-string-test);
8 | }
9 |
10 |
--------------------------------------------------------------------------------
/packages/plugin-css/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "baseUrl": "src",
5 | "outDir": "dist"
6 | },
7 | "include": ["./src/", "./test/"]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/plugin-css/vitest.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vitest/config';
2 |
3 | export default defineConfig({
4 | test: {
5 | mockReset: true,
6 | testTimeout: 30_000, // Only needed for Windows
7 | },
8 | });
9 |
--------------------------------------------------------------------------------
/packages/plugin-js/.npmignore:
--------------------------------------------------------------------------------
1 | .turbo
2 | biome.json
3 | src/**
4 | test/**
5 | tsconfig.*
6 | vitest.*
7 |
--------------------------------------------------------------------------------
/packages/plugin-js/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Drew Powers
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/plugin-js/README.md:
--------------------------------------------------------------------------------
1 | # ⛋ @terrazzo/plugin-js
2 |
3 | Generate JavaScript, TypeScript, and JSON from DTCG tokens.
4 |
5 | ## Setup
6 |
7 | Requires [Node.js 20 or later](https://nodejs.org). With that installed, run:
8 |
9 | ```sh
10 | npm i -D @terrazzo/cli @terrazzo/plugin-js
11 | ```
12 |
13 | Add a `terrazzo.config.js` to the root of your project with:
14 |
15 | ```ts
16 | import { defineConfig } from "@terrazzo/cli";
17 | import js from "@terrazzo/plugin-js";
18 |
19 | export default defineConfig({
20 | outDir: "./tokens/",
21 | plugins: [
22 | js({
23 | js: "index.js",
24 | // json: "tokens.json",
25 | }),
26 | ],
27 | });
28 | ```
29 |
30 | Lastly, run:
31 |
32 | ```sh
33 | npx tz build
34 | ```
35 |
36 | And you’ll see a `./tokens/index.js` file generated in your project.
37 |
38 | [Full Documentation](https://terrazzo.app/docs/integrations/js)
39 |
--------------------------------------------------------------------------------
/packages/plugin-js/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
3 | "extends": ["../../biome.json"],
4 | "files": {
5 | "include": ["./src/", "./test/"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/plugin-js/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@terrazzo/plugin-js",
3 | "version": "0.8.0",
4 | "description": "Generate JS, TS, and JSON from your DTCG design tokens JSON.",
5 | "license": "MIT",
6 | "type": "module",
7 | "author": {
8 | "name": "Drew Powers",
9 | "email": "drew@pow.rs"
10 | },
11 | "keywords": [
12 | "design tokens",
13 | "design system",
14 | "dtcg",
15 | "w3c",
16 | "ts",
17 | "typescript"
18 | ],
19 | "main": "./dist/index.js",
20 | "exports": {
21 | ".": "./dist/index.js",
22 | "./package.json": "./package.json"
23 | },
24 | "homepage": "https://terrazzo.app/docs/cli/integrations/js",
25 | "repository": {
26 | "type": "git",
27 | "url": "https://github.com/terrazzoapp/terrazzo.git",
28 | "directory": "./packages/plugin-js/"
29 | },
30 | "scripts": {
31 | "build": "rolldown -c && attw --profile esm-only --pack .",
32 | "dev": "rolldown -c -w",
33 | "format": "biome check --fix --unsafe .",
34 | "lint": "pnpm --filter @terrazzo/plugin-js run \"/^lint:(js|ts)/\"",
35 | "lint:js": "biome check .",
36 | "lint:ts": "tsc --noEmit",
37 | "test": "vitest run"
38 | },
39 | "peerDependencies": {
40 | "@terrazzo/cli": "^0.8.0"
41 | },
42 | "dependencies": {
43 | "@terrazzo/token-tools": "workspace:^"
44 | },
45 | "devDependencies": {
46 | "@terrazzo/cli": "workspace:^",
47 | "@terrazzo/parser": "workspace:^"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/packages/plugin-js/rolldown.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'rolldown';
2 | import { dts } from 'rolldown-plugin-dts';
3 |
4 | export default defineConfig({
5 | input: {
6 | index: './src/index.ts',
7 | },
8 | plugins: [dts()],
9 | output: {
10 | dir: 'dist',
11 | format: 'es',
12 | sourcemap: true,
13 | },
14 | });
15 |
--------------------------------------------------------------------------------
/packages/plugin-js/test/shadow/want.d.ts:
--------------------------------------------------------------------------------
1 | /** ------------------------------------------
2 | * Autogenerated by ⛋ Terrazzo. DO NOT EDIT!
3 | * ------------------------------------------- */
4 |
5 | import type {
6 | ShadowTokenNormalized,
7 | } from "@terrazzo/parser";
8 |
9 | export declare const tokens: {
10 | "shadow.base": Record<".", ShadowTokenNormalized["$value"]>;
11 | "shadow.simple": Record<".", ShadowTokenNormalized["$value"]>;
12 | "shadow.inset": Record<".", ShadowTokenNormalized["$value"]>;
13 | "shadow.layered": Record<".", ShadowTokenNormalized["$value"]>;
14 | };
15 |
16 | export declare function token(tokenID: K, modeName?: never): (typeof tokens)[K]["."];
17 | export declare function token(tokenID: K, modeName: M): (typeof tokens)[K][M];
18 |
--------------------------------------------------------------------------------
/packages/plugin-js/test/typography/want.d.ts:
--------------------------------------------------------------------------------
1 | /** ------------------------------------------
2 | * Autogenerated by ⛋ Terrazzo. DO NOT EDIT!
3 | * ------------------------------------------- */
4 |
5 | import type {
6 | FontFamilyTokenNormalized,
7 | TypographyTokenNormalized,
8 | } from "@terrazzo/parser";
9 |
10 | export declare const tokens: {
11 | "typography.family.body": Record<".", FontFamilyTokenNormalized["$value"]>;
12 | "typography.family.heading": Record<".", FontFamilyTokenNormalized["$value"]>;
13 | "typography.largeTitle": Record<"." | "xs" | "s" | "m" | "l" | "xl" | "2xl" | "3xl", TypographyTokenNormalized["$value"]>;
14 | "typography.body": Record<"." | "xs" | "s" | "m" | "l" | "xl" | "2xl" | "3xl", TypographyTokenNormalized["$value"]>;
15 | };
16 |
17 | export declare function token(tokenID: K, modeName?: never): (typeof tokens)[K]["."];
18 | export declare function token(tokenID: K, modeName: M): (typeof tokens)[K][M];
19 |
--------------------------------------------------------------------------------
/packages/plugin-js/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "baseUrl": "src",
5 | "outDir": "dist"
6 | },
7 | "include": ["./src/", "./test/"]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/plugin-js/vitest.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vitest/config';
2 | import os from 'node:os';
3 |
4 | export default defineConfig({
5 | test: {
6 | testTimeout: os.platform() === 'win32' ? 10000 : 5000,
7 | },
8 | });
9 |
--------------------------------------------------------------------------------
/packages/plugin-sass/.npmignore:
--------------------------------------------------------------------------------
1 | .turbo
2 | biome.json
3 | src/**
4 | test/**
5 | tsconfig.*
6 | vitest.*
7 |
--------------------------------------------------------------------------------
/packages/plugin-sass/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Drew Powers
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/plugin-sass/README.md:
--------------------------------------------------------------------------------
1 | # ⛋ @terrazzo/plugin-sass
2 |
3 | Convert DTCG tokens into Sass for use in any web application or native app with webview. Uses [the CSS plugin](/docs/integrations/css) under the hood that lets you use all of CSS’ features with the typesafety of Sass.
4 |
5 | ## Setup
6 |
7 | Requires [Node.js 20 or later](https://nodejs.org) and [the CLI installed](https://terrazzo.app/docs/cli). With both installed, run:
8 |
9 | ```sh
10 | npm i -D @terrazzo/plugin-css @terrazzo/plugin-sass
11 | ```
12 |
13 | Add a `terrazzo.config.js` to the root of your project:
14 |
15 | ```ts
16 | import { defineConfig } from "@terrazzo/cli";
17 | import css from "@terrazzo/plugin-css";
18 | import sass from "@terrazzo/plugin-sass";
19 |
20 | export default defineConfig({
21 | outDir: "./tokens/",
22 | plugins: [
23 | css({
24 | filename: "tokens.css",
25 | }),
26 | sass({
27 | filename: "index.sass",
28 | }),
29 | ],
30 | });
31 | ```
32 |
33 | Lastly, run:
34 |
35 | ```sh
36 | npx tz build
37 | ```
38 |
39 | And you’ll see a `./tokens/index.scss` file generated in your project.
40 |
41 | [Full Documentation](https://terrazzo.app/docs/integrations/sass)
42 |
--------------------------------------------------------------------------------
/packages/plugin-sass/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
3 | "extends": ["../../biome.json"],
4 | "files": {
5 | "include": ["./src/", "./test/"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/plugin-sass/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@terrazzo/plugin-sass",
3 | "version": "0.8.0",
4 | "description": "Generate .scss from your DTCG design tokens JSON.",
5 | "license": "MIT",
6 | "type": "module",
7 | "author": {
8 | "name": "Drew Powers",
9 | "email": "drew@pow.rs"
10 | },
11 | "keywords": [
12 | "design tokens",
13 | "dtcg",
14 | "w3c",
15 | "css",
16 | "sass"
17 | ],
18 | "main": "./dist/index.js",
19 | "exports": {
20 | ".": "./dist/index.js",
21 | "./package.json": "./package.json"
22 | },
23 | "homepage": "https://terrazzo.app/docs/cli/integrations/sass",
24 | "repository": {
25 | "type": "git",
26 | "url": "https://github.com/terrazzoapp/terrazzo.git",
27 | "directory": "packages/plugin-sass"
28 | },
29 | "scripts": {
30 | "build": "rolldown -c && attw --profile esm-only --pack .",
31 | "dev": "rolldown -c -w",
32 | "format": "biome check --fix --unsafe .",
33 | "lint": "pnpm --filter @terrazzo/plugin-sass run \"/^lint:(js|ts)/\"",
34 | "lint:js": "biome check .",
35 | "lint:ts": "tsc --noEmit",
36 | "test": "vitest run"
37 | },
38 | "peerDependencies": {
39 | "@terrazzo/cli": "^0.8.0",
40 | "@terrazzo/plugin-css": "^0.8.0"
41 | },
42 | "dependencies": {
43 | "@terrazzo/token-tools": "workspace:^"
44 | },
45 | "devDependencies": {
46 | "@terrazzo/cli": "workspace:^",
47 | "@terrazzo/parser": "workspace:^",
48 | "@terrazzo/plugin-css": "workspace:^"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/packages/plugin-sass/rolldown.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'rolldown';
2 | import { dts } from 'rolldown-plugin-dts';
3 |
4 | export default defineConfig({
5 | input: {
6 | index: './src/index.ts',
7 | },
8 | plugins: [dts()],
9 | output: {
10 | dir: 'dist',
11 | format: 'es',
12 | sourcemap: true,
13 | },
14 | });
15 |
--------------------------------------------------------------------------------
/packages/plugin-sass/src/index.ts:
--------------------------------------------------------------------------------
1 | import type { Plugin } from '@terrazzo/parser';
2 | import build from './build.js';
3 | import type { SassPluginOptions } from './lib.js';
4 |
5 | export * from './lib.js';
6 |
7 | export default function pluginSass(options?: SassPluginOptions): Plugin {
8 | const filename = options?.filename ?? 'index.scss';
9 |
10 | return {
11 | name: '@terrazzo/plugin-sass',
12 | enforce: 'post',
13 | config(config) {
14 | // plugin-css is required for transforms. throw error
15 | if (!config.plugins.some((p) => p.name === '@terrazzo/plugin-css')) {
16 | throw new Error(
17 | `@terrazzo/plugin-sass relies on @terrazzo/plugin-css.
18 | Please install @terrazzo/plugin-css and follow setup to add to your config.`,
19 | );
20 | }
21 | },
22 | async build({ getTransforms, outputFile }) {
23 | const output = build({ getTransforms, options });
24 | outputFile(filename, output);
25 | },
26 | };
27 | }
28 |
--------------------------------------------------------------------------------
/packages/plugin-sass/src/lib.ts:
--------------------------------------------------------------------------------
1 | import type { CSSPluginOptions } from '@terrazzo/plugin-css';
2 |
3 | export interface SassPluginOptions {
4 | /** Where to output CSS */
5 | filename?: CSSPluginOptions['filename'];
6 | /** Glob patterns to exclude tokens from output */
7 | exclude?: CSSPluginOptions['exclude'];
8 | }
9 |
10 | export const FILE_HEADER = `////
11 | /// Autogenerated by ⛋ Terrazzo. DO NOT EDIT!
12 | ////
13 |
14 | @use "sass:list";
15 | @use "sass:map";`;
16 |
17 | export const MIXIN_TOKEN = `@function token($tokenName) {
18 | @if map.has-key($__token-values, $tokenName) == false {
19 | @error 'No token named "#{$tokenName}"';
20 | }
21 | $_token: map.get($__token-values, $tokenName);
22 | @if map.has-key($_token, "__tz-error") {
23 | @error map.get($_token, "__tz-error");
24 | }
25 | @return map.get($_token);
26 | }`;
27 |
28 | export const MIXIN_TYPOGRAPHY = `@mixin typography($tokenName, $modeName: ".") {
29 | @if map.has-key($__token-typography-mixins, $tokenName) == false {
30 | @error 'No typography mixin named "#{$tokenName}"';
31 | }
32 | $_mixin: map.get($__token-typography-mixins, $tokenName);
33 | $_properties: map.get($_mixin, ".");
34 | @if map.has-key($_mixin) {
35 | $_properties: map.get($_mixin);
36 | }
37 | @each $_property, $_value in $_properties {
38 | #{$_property}: #{$_value};
39 | }
40 | }`;
41 |
--------------------------------------------------------------------------------
/packages/plugin-sass/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "baseUrl": "src",
5 | "outDir": "dist"
6 | },
7 | "include": ["./src/", "./test/"]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/plugin-sass/vitest.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vitest/config';
2 | import os from 'node:os';
3 |
4 | export default defineConfig({
5 | test: {
6 | testTimeout: os.platform() === 'win32' ? 10000 : 5000,
7 | },
8 | });
9 |
--------------------------------------------------------------------------------
/packages/plugin-swift/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 |
--------------------------------------------------------------------------------
/packages/plugin-swift/.npmignore:
--------------------------------------------------------------------------------
1 | .turbo
2 | biome.json
3 | src/**
4 | test/**
5 | tsconfig.*
6 | vitest.*
7 |
--------------------------------------------------------------------------------
/packages/plugin-swift/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Drew Powers
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/plugin-swift/README.md:
--------------------------------------------------------------------------------
1 | # ⛋ @terrazzo/plugin-swift
2 |
3 | Generate Swift code from DTCG tokens.
4 |
5 | ## Setup
6 |
7 | Requires [Node.js 20 or later](https://nodejs.org). With that installed, and a `package.json`, run:
8 |
9 | ```sh
10 | npm i -D @terrazzo/cli @terrazzo/plugin-swift
11 | ```
12 |
13 | Add a `terrazzo.config.js` to the root of your project with:
14 |
15 | ```ts
16 | import { defineConfig } from "@terrazzo/cli";
17 | import swift from "@terrazzo/plugin-swift";
18 |
19 | export default defineConfig({
20 | outDir: "./tokens/",
21 | plugins: [
22 | swift({
23 | catalogName: "Tokens",
24 | }),
25 | ],
26 | });
27 | ```
28 |
29 | Lastly, run:
30 |
31 | ```sh
32 | npx tz build
33 | ```
34 |
35 | And you’ll see a `./tokens/Tokens.xcassets` catalog generated in your project. [Import it](https://developer.apple.com/documentation/xcode/managing-assets-with-asset-catalogs) into your project and you’ll have access to all your tokens!
36 |
37 | [Full Documentation](https://terrazzo.app/docs/integrations/swift)
38 |
--------------------------------------------------------------------------------
/packages/plugin-swift/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
3 | "extends": ["../../biome.json"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/plugin-swift/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@terrazzo/plugin-swift",
3 | "version": "0.1.0",
4 | "description": "Generate Swift code using DTCG design tokens JSON.",
5 | "license": "MIT",
6 | "type": "module",
7 | "author": {
8 | "name": "Drew Powers",
9 | "email": "drew@pow.rs"
10 | },
11 | "keywords": [
12 | "design tokens",
13 | "dtcg",
14 | "dtfm",
15 | "w3c",
16 | "swift",
17 | "ios"
18 | ],
19 | "main": "./dist/index.js",
20 | "exports": {
21 | ".": "./dist/index.js",
22 | "./package.json": "./package.json"
23 | },
24 | "homepage": "https://terrazzo.app/docs/cli/integrations/swift",
25 | "repository": {
26 | "type": "git",
27 | "url": "https://github.com/terrazzoapp/terrazzo.git",
28 | "directory": "./packages/plugin-swift/"
29 | },
30 | "scripts": {
31 | "build": "rolldown -c && attw --profile esm-only --pack .",
32 | "dev": "rolldown -c -w",
33 | "format": "biome check --fix --unsafe .",
34 | "lint": "biome check .",
35 | "test": "vitest run"
36 | },
37 | "peerDependencies": {
38 | "@terrazzo/cli": "^0.8.0"
39 | },
40 | "dependencies": {
41 | "@terrazzo/token-tools": "workspace:^",
42 | "culori": "^4.0.1"
43 | },
44 | "devDependencies": {
45 | "@terrazzo/cli": "workspace:*",
46 | "@terrazzo/parser": "workspace:^",
47 | "@types/culori": "^4.0.0"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/packages/plugin-swift/rolldown.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'rolldown';
2 | import { dts } from 'rolldown-plugin-dts';
3 |
4 | export default defineConfig({
5 | input: {
6 | index: './src/index.ts',
7 | },
8 | plugins: [dts()],
9 | output: {
10 | dir: 'dist',
11 | format: 'es',
12 | sourcemap: true,
13 | },
14 | });
15 |
--------------------------------------------------------------------------------
/packages/plugin-swift/test/color/Want.xcassets/color.primitive.blue.1.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors": [
3 | {
4 | "color": {
5 | "color-space": "display-p3",
6 | "components": {
7 | "red": "0.9857122261910846",
8 | "green": "0.9918978348582416",
9 | "blue": "0.9991694919090187",
10 | "alpha": "1"
11 | }
12 | },
13 | "idiom": "universal"
14 | },
15 | {
16 | "appearances": [
17 | {
18 | "appearance": "luminosity",
19 | "value": "dark"
20 | }
21 | ],
22 | "color": {
23 | "color-space": "display-p3",
24 | "components": {
25 | "red": "0.05746783530090841",
26 | "green": "0.08146881860449817",
27 | "blue": "0.12189566404846394",
28 | "alpha": "1"
29 | }
30 | },
31 | "idiom": "universal"
32 | }
33 | ],
34 | "info": {
35 | "author": "Terrazzo",
36 | "version": 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/plugin-swift/test/color/Want.xcassets/color.primitive.blue.10.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors": [
3 | {
4 | "color": {
5 | "color-space": "display-p3",
6 | "components": {
7 | "red": "0.23468314487818098",
8 | "green": "0.525167106120089",
9 | "blue": "0.9119403311366554",
10 | "alpha": "1"
11 | }
12 | },
13 | "idiom": "universal"
14 | },
15 | {
16 | "appearances": [
17 | {
18 | "appearance": "luminosity",
19 | "value": "dark"
20 | }
21 | ],
22 | "color": {
23 | "color-space": "display-p3",
24 | "components": {
25 | "red": "0.3435321435041201",
26 | "green": "0.6114011043023767",
27 | "blue": "0.9713324919373404",
28 | "alpha": "1"
29 | }
30 | },
31 | "idiom": "universal"
32 | }
33 | ],
34 | "info": {
35 | "author": "Terrazzo",
36 | "version": 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/plugin-swift/test/color/Want.xcassets/color.primitive.blue.11.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors": [
3 | {
4 | "color": {
5 | "color-space": "display-p3",
6 | "components": {
7 | "red": "0.2038490640365916",
8 | "green": "0.44794576348361775",
9 | "blue": "0.7825620608925641",
10 | "alpha": "1"
11 | }
12 | },
13 | "idiom": "universal"
14 | },
15 | {
16 | "appearances": [
17 | {
18 | "appearance": "luminosity",
19 | "value": "dark"
20 | }
21 | ],
22 | "color": {
23 | "color-space": "display-p3",
24 | "components": {
25 | "red": "0.5046395218152211",
26 | "green": "0.7144126403383976",
27 | "blue": "0.9767803153140814",
28 | "alpha": "1"
29 | }
30 | },
31 | "idiom": "universal"
32 | }
33 | ],
34 | "info": {
35 | "author": "Terrazzo",
36 | "version": 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/plugin-swift/test/color/Want.xcassets/color.primitive.blue.12.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors": [
3 | {
4 | "color": {
5 | "color-space": "display-p3",
6 | "components": {
7 | "red": "0.10160032180994383",
8 | "green": "0.1931928893247831",
9 | "blue": "0.3787199032361936",
10 | "alpha": "1"
11 | }
12 | },
13 | "idiom": "universal"
14 | },
15 | {
16 | "appearances": [
17 | {
18 | "appearance": "luminosity",
19 | "value": "dark"
20 | }
21 | ],
22 | "color": {
23 | "color-space": "display-p3",
24 | "components": {
25 | "red": "0.7883005117642531",
26 | "green": "0.8977356928858071",
27 | "blue": "0.9898314408312648",
28 | "alpha": "1"
29 | }
30 | },
31 | "idiom": "universal"
32 | }
33 | ],
34 | "info": {
35 | "author": "Terrazzo",
36 | "version": 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/plugin-swift/test/color/Want.xcassets/color.primitive.blue.2.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors": [
3 | {
4 | "color": {
5 | "color-space": "display-p3",
6 | "components": {
7 | "red": "0.9610959140132492",
8 | "green": "0.9796231037551396",
9 | "blue": "0.9978799613348245",
10 | "alpha": "1"
11 | }
12 | },
13 | "idiom": "universal"
14 | },
15 | {
16 | "appearances": [
17 | {
18 | "appearance": "luminosity",
19 | "value": "dark"
20 | }
21 | ],
22 | "color": {
23 | "color-space": "display-p3",
24 | "components": {
25 | "red": "0.07303985547217764",
26 | "green": "0.09713934555099096",
27 | "blue": "0.14853779817781101",
28 | "alpha": "1"
29 | }
30 | },
31 | "idiom": "universal"
32 | }
33 | ],
34 | "info": {
35 | "author": "Terrazzo",
36 | "version": 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/plugin-swift/test/color/Want.xcassets/color.primitive.blue.3.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors": [
3 | {
4 | "color": {
5 | "color-space": "display-p3",
6 | "components": {
7 | "red": "0.9120281437931872",
8 | "green": "0.9551068985440067",
9 | "blue": "0.9917926478801694",
10 | "alpha": "1"
11 | }
12 | },
13 | "idiom": "universal"
14 | },
15 | {
16 | "appearances": [
17 | {
18 | "appearance": "luminosity",
19 | "value": "dark"
20 | }
21 | ],
22 | "color": {
23 | "color-space": "display-p3",
24 | "components": {
25 | "red": "0.07908044574694958",
26 | "green": "0.15446938825505227",
27 | "blue": "0.26936679657969886",
28 | "alpha": "1"
29 | }
30 | },
31 | "idiom": "universal"
32 | }
33 | ],
34 | "info": {
35 | "author": "Terrazzo",
36 | "version": 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/plugin-swift/test/color/Want.xcassets/color.primitive.blue.4.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors": [
3 | {
4 | "color": {
5 | "color-space": "display-p3",
6 | "components": {
7 | "red": "0.85457584989346",
8 | "green": "0.9341034497224382",
9 | "blue": "0.9931007763338738",
10 | "alpha": "1"
11 | }
12 | },
13 | "idiom": "universal"
14 | },
15 | {
16 | "appearances": [
17 | {
18 | "appearance": "luminosity",
19 | "value": "dark"
20 | }
21 | ],
22 | "color": {
23 | "color-space": "display-p3",
24 | "components": {
25 | "red": "0.06909232748693128",
26 | "green": "0.19643835900273826",
27 | "blue": "0.37125592617605824",
28 | "alpha": "1"
29 | }
30 | },
31 | "idiom": "universal"
32 | }
33 | ],
34 | "info": {
35 | "author": "Terrazzo",
36 | "version": 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/plugin-swift/test/color/Want.xcassets/color.primitive.blue.5.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors": [
3 | {
4 | "color": {
5 | "color-space": "display-p3",
6 | "components": {
7 | "red": "0.787471272606805",
8 | "green": "0.8939209890163644",
9 | "blue": "0.9895810628966571",
10 | "alpha": "1"
11 | }
12 | },
13 | "idiom": "universal"
14 | },
15 | {
16 | "appearances": [
17 | {
18 | "appearance": "luminosity",
19 | "value": "dark"
20 | }
21 | ],
22 | "color": {
23 | "color-space": "display-p3",
24 | "components": {
25 | "red": "0.09390125109062433",
26 | "green": "0.24670669682719953",
27 | "blue": "0.4401071689219961",
28 | "alpha": "1"
29 | }
30 | },
31 | "idiom": "universal"
32 | }
33 | ],
34 | "info": {
35 | "author": "Terrazzo",
36 | "version": 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/plugin-swift/test/color/Want.xcassets/color.primitive.blue.6.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors": [
3 | {
4 | "color": {
5 | "color-space": "display-p3",
6 | "components": {
7 | "red": "0.7092079991743006",
8 | "green": "0.8420585712777626",
9 | "blue": "0.9745609882360672",
10 | "alpha": "1"
11 | }
12 | },
13 | "idiom": "universal"
14 | },
15 | {
16 | "appearances": [
17 | {
18 | "appearance": "luminosity",
19 | "value": "dark"
20 | }
21 | ],
22 | "color": {
23 | "color-space": "display-p3",
24 | "components": {
25 | "red": "0.14022516582440694",
26 | "green": "0.2973263472969356",
27 | "blue": "0.5127819131506204",
28 | "alpha": "1"
29 | }
30 | },
31 | "idiom": "universal"
32 | }
33 | ],
34 | "info": {
35 | "author": "Terrazzo",
36 | "version": 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/plugin-swift/test/color/Want.xcassets/color.primitive.blue.7.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors": [
3 | {
4 | "color": {
5 | "color-space": "display-p3",
6 | "components": {
7 | "red": "0.605510599682131",
8 | "green": "0.7781097681313138",
9 | "blue": "0.9479029174691492",
10 | "alpha": "1"
11 | }
12 | },
13 | "idiom": "universal"
14 | },
15 | {
16 | "appearances": [
17 | {
18 | "appearance": "luminosity",
19 | "value": "dark"
20 | }
21 | ],
22 | "color": {
23 | "color-space": "display-p3",
24 | "components": {
25 | "red": "0.19415626909440487",
26 | "green": "0.3596239398578181",
27 | "blue": "0.6008085975749465",
28 | "alpha": "1"
29 | }
30 | },
31 | "idiom": "universal"
32 | }
33 | ],
34 | "info": {
35 | "author": "Terrazzo",
36 | "version": 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/plugin-swift/test/color/Want.xcassets/color.primitive.blue.8.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors": [
3 | {
4 | "color": {
5 | "color-space": "display-p3",
6 | "components": {
7 | "red": "0.4493472380669719",
8 | "green": "0.6863379927169785",
9 | "blue": "0.9161077085265298",
10 | "alpha": "1"
11 | }
12 | },
13 | "idiom": "universal"
14 | },
15 | {
16 | "appearances": [
17 | {
18 | "appearance": "luminosity",
19 | "value": "dark"
20 | }
21 | ],
22 | "color": {
23 | "color-space": "display-p3",
24 | "components": {
25 | "red": "0.23803842782286488",
26 | "green": "0.43322463113208826",
27 | "blue": "0.71894276694965",
28 | "alpha": "1"
29 | }
30 | },
31 | "idiom": "universal"
32 | }
33 | ],
34 | "info": {
35 | "author": "Terrazzo",
36 | "version": 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/plugin-swift/test/color/Want.xcassets/color.primitive.blue.9.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors": [
3 | {
4 | "color": {
5 | "color-space": "display-p3",
6 | "components": {
7 | "red": "0.2465715501902695",
8 | "green": "0.5560503142085003",
9 | "blue": "0.9689026731386735",
10 | "alpha": "1"
11 | }
12 | },
13 | "idiom": "universal"
14 | },
15 | {
16 | "appearances": [
17 | {
18 | "appearance": "luminosity",
19 | "value": "dark"
20 | }
21 | ],
22 | "color": {
23 | "color-space": "display-p3",
24 | "components": {
25 | "red": "0.2465715501902695",
26 | "green": "0.5560503142085003",
27 | "blue": "0.9689026731386735",
28 | "alpha": "1"
29 | }
30 | },
31 | "idiom": "universal"
32 | }
33 | ],
34 | "info": {
35 | "author": "Terrazzo",
36 | "version": 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/plugin-swift/test/index.test.ts:
--------------------------------------------------------------------------------
1 | import fs from 'node:fs';
2 | import { fileURLToPath } from 'node:url';
3 | import { build, defineConfig, parse } from '@terrazzo/parser';
4 | import { describe, expect, it } from 'vitest';
5 | import swift from '../src/index.js';
6 |
7 | describe('@terrazzo/plugin-swift', () => {
8 | it('basic', async () => {
9 | const cwd = new URL('./color/', import.meta.url);
10 | const config = defineConfig(
11 | {
12 | outDir: 'color',
13 | plugins: [
14 | swift({
15 | catalogName: 'Want',
16 | }),
17 | ],
18 | },
19 | { cwd },
20 | );
21 | const tokensJSON = new URL('./tokens.json', cwd);
22 | const { tokens, sources } = await parse([{ filename: tokensJSON, src: fs.readFileSync(tokensJSON, 'utf8') }], {
23 | config,
24 | });
25 | const result = await build(tokens, { config, sources });
26 | for (const { filename, contents } of result.outputFiles) {
27 | await expect(contents).toMatchFileSnapshot(fileURLToPath(new URL(filename, cwd)));
28 | }
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/packages/plugin-swift/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "baseUrl": "src",
5 | "outDir": "dist"
6 | },
7 | "include": ["./src/", "./test/**/*.test.ts"]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/plugin-tailwind/.gitignore:
--------------------------------------------------------------------------------
1 | test/fixtures/**/actual.css
2 |
--------------------------------------------------------------------------------
/packages/plugin-tailwind/.npmignore:
--------------------------------------------------------------------------------
1 | .turbo
2 | biome.json
3 | src/**
4 | test/**
5 | tsconfig.*
6 | vitest.*
7 |
--------------------------------------------------------------------------------
/packages/plugin-tailwind/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Drew Powers
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/plugin-tailwind/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
3 | "extends": ["../../biome.json"],
4 | "files": {
5 | "include": ["./src/", "./test/"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/plugin-tailwind/rolldown.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'rolldown';
2 | import { dts } from 'rolldown-plugin-dts';
3 |
4 | export default defineConfig({
5 | input: {
6 | index: './src/index.ts',
7 | },
8 | plugins: [dts()],
9 | output: {
10 | dir: 'dist',
11 | format: 'es',
12 | sourcemap: true,
13 | },
14 | });
15 |
--------------------------------------------------------------------------------
/packages/plugin-tailwind/src/lib.ts:
--------------------------------------------------------------------------------
1 | export interface TailwindPluginOptions {
2 | /**
3 | * Filename to output.
4 | * @default "tailwind-theme.css"
5 | */
6 | filename?: string;
7 | /** @see https://tailwindcss.com/docs/theme */
8 | theme: Record;
9 | /** Array of mapping variants to DTCG modes. */
10 | modeVariants?: { variant: string; mode: string }[];
11 | }
12 |
13 | /** Flatten an arbitrarily-nested object */
14 | export function flattenThemeObj(themeObj: Record): { path: string[]; value: string | string[] }[] {
15 | const result: { path: string[]; value: string | string[] }[] = [];
16 |
17 | function traverse(obj: Record, path: string[]) {
18 | for (const [key, value] of Object.entries(obj)) {
19 | const newPath = [...path, key];
20 | if (typeof value === 'string' || Array.isArray(value)) {
21 | if (Array.isArray(value) && (value.length === 0 || value.some((v) => typeof v !== 'string'))) {
22 | throw new Error(
23 | `Invalid value at path "${newPath.join('.')}": expected a string or an array of strings, but got ${JSON.stringify(value)}`,
24 | );
25 | }
26 | result.push({ path: newPath, value });
27 | } else if (typeof value === 'object' && value !== null) {
28 | traverse(value as Record, newPath);
29 | }
30 | }
31 | }
32 |
33 | traverse(themeObj, []);
34 | return result;
35 | }
36 |
--------------------------------------------------------------------------------
/packages/plugin-tailwind/test/cli.test.ts:
--------------------------------------------------------------------------------
1 | import fs from 'node:fs';
2 | import { fileURLToPath } from 'node:url';
3 | import { defineConfig } from '@terrazzo/parser';
4 | import { execa } from 'execa';
5 | import { describe, expect, test } from 'vitest';
6 | import tailwind from '../src/index.js';
7 |
8 | const CMD = '../../../../cli/bin/cli.js';
9 |
10 | describe('CLI', () => {
11 | const fixtures = ['primer'];
12 |
13 | test.each(fixtures)('%s', async (dir) => {
14 | const cwd = new URL(`./fixtures/${dir}/`, import.meta.url);
15 | await execa('node', [CMD, 'build'], { cwd, stdout: 'inherit' });
16 | await expect(fs.readFileSync(new URL('./actual.css', cwd), 'utf8')).toMatchFileSnapshot(
17 | fileURLToPath(new URL('./want.css', cwd)),
18 | );
19 | });
20 |
21 | describe('errors', () => {
22 | test('plugin-css missing', async () => {
23 | const cwd = new URL('./fixtures/primer/', import.meta.url);
24 | expect(() => defineConfig({ plugins: [tailwind({ theme: {} })] }, { cwd })).toThrowError(
25 | '@terrazzo/plugin-css missing! Please install and add to the plugins array to use the Tailwind plugin.',
26 | );
27 | });
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/packages/plugin-tailwind/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "baseUrl": "src",
5 | "outDir": "dist"
6 | },
7 | "include": ["./src/", "./test/"]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/plugin-tailwind/vitest.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vitest/config';
2 | import os from 'node:os';
3 |
4 | export default defineConfig({
5 | test: {
6 | testTimeout: os.platform() === 'win32' ? 10000 : 5000,
7 | },
8 | });
9 |
--------------------------------------------------------------------------------
/packages/react-color-picker/.npmignore:
--------------------------------------------------------------------------------
1 | *.test.*
2 | .size-limit.js
3 | .turbo
4 | biome.*
5 | rollup.*
6 | src/**
7 | stylelint.*
8 | vite.*
9 | vitest.*
10 | tsconfig.*
11 |
--------------------------------------------------------------------------------
/packages/react-color-picker/.size-limit.js:
--------------------------------------------------------------------------------
1 | export default [
2 | {
3 | ignore: ['@use-gesture/react', '@terrazzo/icons', '@terrazzo/tiles', '@terrazzo/use-color', 'culori', 'react'],
4 | path: './dist/index.js',
5 | limit: '15 kB',
6 | },
7 | ];
8 |
--------------------------------------------------------------------------------
/packages/react-color-picker/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @terrazzo/react-color-picker
2 |
3 | ## 0.0.7
4 |
5 | ### Patch Changes
6 |
7 | - Bugfixes thanks to [@lilnasy](lilnasy)
8 |
--------------------------------------------------------------------------------
/packages/react-color-picker/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Drew Powers
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/react-color-picker/README.md:
--------------------------------------------------------------------------------
1 | # @terrazzo/react-color-picker
2 |
3 | Pick colors using CSS Color Module 4, wide color gamut (WCG), and all supported web colorspaces using React and WebGL for monitor-accurate colors. Powered by 🌈 [Culori](https://culorijs.org/).
4 |
5 | `2 kB`, enforced by [size-limit](https://www.npmjs.com/package/size-limit)
6 |
7 | ## Setup
8 |
9 | ```sh
10 | pnpm i @terrazzo/react-color-picker
11 | ```
12 |
13 | ```tsx
14 | import ColorPicker from '@terrazzo/react-color-picker';
15 | import { useState } from 'react';
16 |
17 | const [color, setColor] = useState('color(display-p3 0 0.3 1)');
18 |
19 | ;
20 | ```
21 |
--------------------------------------------------------------------------------
/packages/react-color-picker/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
3 | "extends": ["../../biome.json"],
4 | "linter": {
5 | "rules": {
6 | "correctness": {
7 | "useHookAtTopLevel": "off"
8 | }
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/react-color-picker/rollup.config.js:
--------------------------------------------------------------------------------
1 | import ts from '@rollup/plugin-typescript';
2 | import css from 'rollup-plugin-import-css';
3 |
4 | /** @type {import("rollup").InputOptions} */
5 | export default {
6 | plugins: [
7 | css({
8 | output: 'all-components.css',
9 | }),
10 | ts({
11 | tsconfig: './tsconfig.build.json',
12 | }),
13 | ],
14 | input: 'src/index.ts',
15 | external: ['*'],
16 | output: {
17 | dir: './dist/',
18 | sourcemap: true,
19 | globals: {
20 | 'react/jsx-runtime': 'jsxRuntime',
21 | 'react-dom/client': 'ReactDOM',
22 | react: 'React',
23 | },
24 | },
25 | };
26 |
--------------------------------------------------------------------------------
/packages/react-color-picker/src/components/HueWheel.tsx:
--------------------------------------------------------------------------------
1 | import { type ComponentProps, useEffect, useRef, useState } from 'react';
2 | import { HueWheel as HueWheelWebGL } from '../lib/webgl.js';
3 |
4 | export type HueWheelProps = ComponentProps<'canvas'>;
5 |
6 | function HueWheel({ ...rest }: HueWheelProps) {
7 | const canvasEl = useRef(null);
8 | const [webgl, setWebgl] = useState();
9 |
10 | // initialize
11 | useEffect(() => {
12 | if (webgl || !canvasEl.current) {
13 | return;
14 | }
15 | setWebgl(new HueWheelWebGL({ canvas: canvasEl.current }));
16 | }, [webgl]);
17 |
18 | return ;
19 | }
20 |
21 | export default HueWheel;
22 |
--------------------------------------------------------------------------------
/packages/react-color-picker/src/components/TrueGradient.tsx:
--------------------------------------------------------------------------------
1 | import type { Oklab } from '@terrazzo/use-color';
2 | import { type ComponentProps, useEffect, useRef, useState } from 'react';
3 | import { GradientOklab } from '../lib/webgl.js';
4 |
5 | export interface TrueGradientProps extends ComponentProps<'canvas'> {
6 | start: Oklab;
7 | end: Oklab;
8 | }
9 |
10 | function TrueGradient({ start, end, ...props }: TrueGradientProps) {
11 | const canvasEl = useRef(null);
12 | const [webgl, setWebgl] = useState();
13 |
14 | // initialize
15 | useEffect(() => {
16 | if (webgl || !canvasEl.current) {
17 | return;
18 | }
19 | setWebgl(new GradientOklab({ canvas: canvasEl.current, startColor: start, endColor: end }));
20 | }, [webgl]);
21 |
22 | // update color
23 | useEffect(() => {
24 | if (webgl) {
25 | webgl.setColors(start, end);
26 | }
27 | }, [start, end, webgl]);
28 |
29 | return ;
30 | }
31 |
32 | export default TrueGradient;
33 |
--------------------------------------------------------------------------------
/packages/react-color-picker/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './components/ColorChannelSlider.js';
2 | export { default as ColorChannelSlider } from './components/ColorChannelSlider.js';
3 | export * from './components/ColorPicker.js';
4 | export { default } from './components/ColorPicker.js';
5 | export * from './components/HueWheel.js';
6 | export { default as HueWheel } from './components/HueWheel.js';
7 | export * from './components/TrueGradient.js';
8 | export { default as TrueGradient } from './components/TrueGradient.js';
9 | export * from './lib/color.js';
10 | export * from './lib/oklab.js';
11 | export * from './lib/rgb.js';
12 | export * from './lib/webgl.js';
13 |
--------------------------------------------------------------------------------
/packages/react-color-picker/src/lib/rgb.ts:
--------------------------------------------------------------------------------
1 | /** Generic implementation of the sRGB transfer function */
2 | export const LINEAR_RGB = `float srgb_transfer_fn(float a) {
3 | float v = abs(a);
4 | return v <= 0.0031308 ? 12.92 * v : 1.055 * pow(v, (1.0 / 2.4)) - 0.055;
5 | }
6 |
7 | float srgb_inverse_transfer_fn(float a) {
8 | float v = abs(a);
9 | return v <= 0.04045 ? v / 12.92 : pow((v + 0.055) / 1.055, 2.4);
10 | }
11 |
12 | vec4 srgb_to_linear_rgb(vec4 srgb) {
13 | return vec4(srgb_inverse_transfer_fn(srgb.x), srgb_inverse_transfer_fn(srgb.y), srgb_inverse_transfer_fn(srgb.z), srgb.w);
14 | }
15 |
16 | vec4 linear_rgb_to_srgb(vec4 linear_rgb) {
17 | return vec4(srgb_transfer_fn(linear_rgb.x), srgb_transfer_fn(linear_rgb.y), srgb_transfer_fn(linear_rgb.z), linear_rgb.w);
18 | }
19 |
20 | // Blend 2 vec4 colors together
21 | vec4 avg_vec4(vec4 a, vec4 b, float w) {
22 | float _w = 1.0 - w;
23 | return (a * _w) + (b * w);
24 | }
25 |
26 | vec4 blend_srgb(vec4 a, vec4 b, float w) {
27 | vec4 lrgb_a = srgb_to_linear_rgb(a);
28 | vec4 lrgb_b = srgb_to_linear_rgb(b);
29 | vec4 blended = linear_rgb_to_srgb(avg_vec4(a, b, w));
30 | blended.w = 1.0;
31 | return blended;
32 | }
33 | `;
34 |
--------------------------------------------------------------------------------
/packages/react-color-picker/src/types.d.ts:
--------------------------------------------------------------------------------
1 | import 'react';
2 |
3 | declare module 'react' {
4 | interface CSSProperties {
5 | [key: `--${string}`]: string | number | undefined;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/react-color-picker/stylelint.config.js:
--------------------------------------------------------------------------------
1 | import base from '../../stylelint.config.js';
2 |
3 | /** @type {import('stylelint').Config} */
4 | export default {
5 | ...base,
6 | };
7 |
--------------------------------------------------------------------------------
/packages/react-color-picker/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "module": "NodeNext",
5 | "moduleResolution": "nodenext"
6 | },
7 | "exclude": ["**/__test__/**", "**/*.test.*"]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/react-color-picker/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.react.json",
3 | "compilerOptions": {
4 | "baseUrl": "src",
5 | "outDir": "dist"
6 | },
7 | "include": ["./src/"]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/react-color-picker/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from '@vitejs/plugin-react-swc';
2 | import { defineConfig } from 'vitest/config';
3 |
4 | export default defineConfig({
5 | plugins: [react()],
6 | test: {
7 | environment: 'jsdom',
8 | environmentOptions: {
9 | jsdom: {
10 | resources: 'usable',
11 | },
12 | },
13 | poolOptions: {
14 | threads: {
15 | minThreads: 0,
16 | maxThreads: 1,
17 | },
18 | },
19 | restoreMocks: true,
20 | setupFiles: ['./vitest.setup.ts'],
21 | testTimeout: 15000,
22 | },
23 | });
24 |
--------------------------------------------------------------------------------
/packages/storybook/.gitignore:
--------------------------------------------------------------------------------
1 | storybook/**
2 |
--------------------------------------------------------------------------------
/packages/storybook/.storybook/global.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: var(--tz-font-sans);
3 | font-size: var(--tz-font-body-font-size);
4 | font-variation-settings: var(--tz-font-body-font-variation-settings);
5 | font-weight: var(--tz-font-body-font-weight);
6 | line-height: 1;
7 | margin: 0;
8 | }
9 |
10 | *,
11 | *::before,
12 | *::after {
13 | box-sizing: border-box;
14 | }
15 |
16 | .sb-show-main {
17 | background: color(srgb 1 1 1);
18 | }
19 |
20 | @media (prefers-color-scheme: dark) {
21 | .sb-show-main {
22 | background: color(srgb 0 0 0);
23 | color: color(srgb 1 1 1);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/storybook/.storybook/main.js:
--------------------------------------------------------------------------------
1 | import path from 'node:path';
2 |
3 | /** @type { import('@storybook/react-vite').StorybookConfig } */
4 | const config = {
5 | stories: ['../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
6 | addons: [getAbsolutePath('@storybook/addon-essentials')],
7 |
8 | framework: {
9 | name: getAbsolutePath('@storybook/react-vite'),
10 | options: {},
11 | disableTelemetry: true,
12 | },
13 |
14 | docs: {
15 | autodocs: false,
16 | docsMode: false,
17 | },
18 |
19 | typescript: {
20 | reactDocgen: 'react-docgen-typescript',
21 | },
22 | };
23 | export default config;
24 |
25 | function getAbsolutePath(value) {
26 | return path.dirname(require.resolve(path.join(value, 'package.json')));
27 | }
28 |
--------------------------------------------------------------------------------
/packages/storybook/.storybook/preview.js:
--------------------------------------------------------------------------------
1 | import '@terrazzo/tokens/dist/index.css';
2 | import '@terrazzo/fonts/fragment-mono.css';
3 | import '@terrazzo/fonts/instrument-sans.css';
4 | import '@terrazzo/tiles/dist/all-components.css';
5 | import '@terrazzo/react-color-picker/dist/all-components.css';
6 | import './global.css';
7 |
8 | /** @type { import('@storybook/react').Preview } */
9 | const preview = {
10 | parameters: {
11 | controls: {
12 | matchers: {
13 | color: /(background|color)$/i,
14 | date: /Date$/i,
15 | },
16 | },
17 | },
18 | };
19 |
20 | export default preview;
21 |
--------------------------------------------------------------------------------
/packages/storybook/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Drew Powers
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/storybook/README.md:
--------------------------------------------------------------------------------
1 | # ⛋ @terrazzo/storybook
2 |
3 | Internal package.
4 |
5 | ## Setup
6 |
7 | ```sh
8 | pnpm i
9 | pnpm run sb
10 | ```
11 |
--------------------------------------------------------------------------------
/packages/storybook/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
3 | "extends": ["../../biome.json"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/storybook/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@terrazzo/storybook",
3 | "version": "0.0.0",
4 | "private": true,
5 | "license": "MIT",
6 | "type": "module",
7 | "scripts": {
8 | "dev": "pnpm run sb",
9 | "build:app": "storybook build -o storybook",
10 | "format": "biome check --fix --unsafe src",
11 | "lint": "pnpm run lint:js && pnpm run lint:css",
12 | "lint:js": "biome check src",
13 | "lint:css": "stylelint \"src/**/*.css\"",
14 | "sb": "storybook dev -p 9009"
15 | },
16 | "dependencies": {
17 | "culori": "^4.0.1"
18 | },
19 | "devDependencies": {
20 | "@storybook/addon-essentials": "^8.6.12",
21 | "@storybook/react": "^8.6.12",
22 | "@storybook/react-vite": "^8.6.12",
23 | "@storybook/test": "^8.6.12",
24 | "@terrazzo/fonts": "workspace:^",
25 | "@terrazzo/icons": "workspace:^",
26 | "@terrazzo/react-color-picker": "workspace:^",
27 | "@terrazzo/tiles": "workspace:^",
28 | "@terrazzo/tokens": "workspace:^",
29 | "@terrazzo/use-color": "workspace:^",
30 | "@types/culori": "^4.0.0",
31 | "@types/react": "^19.1.3",
32 | "@types/react-dom": "^19.1.3",
33 | "@vitejs/plugin-react-swc": "^3.9.0",
34 | "react": "19.0.0",
35 | "react-dom": "19.0.0",
36 | "storybook": "^8.6.12",
37 | "vite": "^6.3.5"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/packages/storybook/src/ColorPicker.stories.jsx:
--------------------------------------------------------------------------------
1 | import ColorPicker from '@terrazzo/react-color-picker';
2 | import useColor from '@terrazzo/use-color';
3 |
4 | export default {
5 | title: 'Components/Form/ColorPicker',
6 | component: ColorPicker,
7 | parameters: {
8 | layout: 'centered',
9 | },
10 | };
11 |
12 | export const Overview = {
13 | args: {},
14 | render(args) {
15 | const [color, setColor] = useColor('rgb(33% 33% 100%/1)');
16 | return ;
17 | },
18 | };
19 |
--------------------------------------------------------------------------------
/packages/storybook/src/Icon.stories.jsx:
--------------------------------------------------------------------------------
1 | import * as allIcons from '@terrazzo/icons';
2 |
3 | export default {
4 | title: 'Icons',
5 | };
6 |
7 | export const Icons = {
8 | render() {
9 | return (
10 |
20 | {Object.entries(allIcons).map(([name, Icon]) => (
21 |
32 |
33 | {name}
34 |
35 | ))}
36 |
37 | );
38 | },
39 | };
40 |
--------------------------------------------------------------------------------
/packages/storybook/src/Kbd.stories.jsx:
--------------------------------------------------------------------------------
1 | import { Kbd } from '@terrazzo/tiles';
2 |
3 | /** @type {import("@storybook/react").Meta} */
4 | export default {
5 | title: 'Components/Display/Kbd',
6 | component: Kbd,
7 | parameters: {
8 | layout: 'centered',
9 | },
10 | };
11 |
12 | export const Overview = {
13 | render() {
14 | return F ;
15 | },
16 | };
17 |
--------------------------------------------------------------------------------
/packages/storybook/src/OmniBar.stories.jsx:
--------------------------------------------------------------------------------
1 | import { OmniBar, OmniBarResult } from '@terrazzo/tiles';
2 |
3 | export default {
4 | title: 'Components/Form/OmniBar',
5 | component: OmniBar,
6 | parameters: {
7 | // layout: 'centered',
8 | },
9 | };
10 |
11 | export const Overview = {
12 | render() {
13 | return (
14 | Results 1–3 of 3.>}>
15 |
16 | Search
17 | Result blah blah blah
18 |
19 |
20 | Search
21 | Result blah blah blah
22 |
23 |
24 | Search
25 | Result blah blah blah
26 |
27 |
28 | );
29 | },
30 | };
31 |
--------------------------------------------------------------------------------
/packages/storybook/src/Select.stories.jsx:
--------------------------------------------------------------------------------
1 | import { ColorFilterOutline } from '@terrazzo/icons';
2 | import { Select, SelectGroup, SelectItem } from '@terrazzo/tiles';
3 |
4 | export default {
5 | title: 'Components/Form/Select',
6 | component: Select,
7 | parameters: {
8 | layout: 'centered',
9 | },
10 | };
11 |
12 | export const Overview = {
13 | render() {
14 | return (
15 |
18 |
19 | RGB
20 | >
21 | }
22 | >
23 |
24 | }>
25 | RGB
26 |
27 | }>
28 | Oklab
29 |
30 | }>
31 | Oklch
32 |
33 |
34 |
35 | );
36 | },
37 | };
38 |
--------------------------------------------------------------------------------
/packages/storybook/src/Slider.stories.jsx:
--------------------------------------------------------------------------------
1 | import { Slider } from '@terrazzo/tiles';
2 | import { useState } from 'react';
3 |
4 | export default {
5 | title: 'Components/Form/Slider',
6 | component: Slider,
7 | parameters: {
8 | layout: 'centered',
9 | },
10 | };
11 |
12 | export const Overview = {
13 | args: {
14 | label: 'My Slider',
15 | orientation: 'horizontal',
16 | min: 0,
17 | max: 100,
18 | step: 1,
19 | defaultValue: 0,
20 | },
21 | argTypes: {
22 | orientation: {
23 | control: 'select',
24 | options: ['horizontal', 'vertical'],
25 | },
26 | },
27 | render({ defaultValue, ...args }) {
28 | const [value, setValue] = useState(defaultValue);
29 |
30 | return (
31 |
32 |
33 |
34 | );
35 | },
36 | };
37 |
--------------------------------------------------------------------------------
/packages/storybook/src/SubtleInput.stories.jsx:
--------------------------------------------------------------------------------
1 | import { SubtleInput } from '@terrazzo/tiles';
2 |
3 | export default {
4 | title: 'Components/Form/SubtleInput',
5 | component: SubtleInput,
6 | parameters: {
7 | layout: 'centered',
8 | },
9 | };
10 |
11 | export const Overview = {
12 | args: {
13 | type: 'number',
14 | defaultValue: 23.45,
15 | },
16 | render(args) {
17 | return ;
18 | },
19 | };
20 |
--------------------------------------------------------------------------------
/packages/storybook/src/Switch.stories.jsx:
--------------------------------------------------------------------------------
1 | import { Switch } from '@terrazzo/tiles';
2 |
3 | export default {
4 | title: 'Components/Form/Switch',
5 | component: Switch,
6 | parameters: {
7 | layout: 'centered',
8 | },
9 | };
10 |
11 | export const Overview = {
12 | args: {
13 | label: 'Rec 2020',
14 | },
15 | render(args) {
16 | return ;
17 | },
18 | };
19 |
--------------------------------------------------------------------------------
/packages/storybook/src/TokenType.stories.jsx:
--------------------------------------------------------------------------------
1 | import { TokenType } from '@terrazzo/tiles';
2 |
3 | export default {
4 | title: 'Components/Display/TokenType',
5 | component: TokenType,
6 | parameters: {
7 | layout: 'centered',
8 | },
9 | };
10 |
11 | export const Overview = {
12 | render(args) {
13 | return (
14 |
22 | {[
23 | 'color',
24 | 'dimension',
25 | 'fontFamily',
26 | 'fontWeight',
27 | 'duration',
28 | 'cubicBezier',
29 | 'number',
30 | 'link',
31 | 'strokeStyle',
32 | 'border',
33 | 'transition',
34 | 'shadow',
35 | 'gradient',
36 | 'typography',
37 | ].map((type) => (
38 |
39 | ))}
40 |
41 | );
42 | },
43 | };
44 |
--------------------------------------------------------------------------------
/packages/storybook/src/TrueGradient.stories.jsx:
--------------------------------------------------------------------------------
1 | import { TrueGradient } from '@terrazzo/react-color-picker';
2 | import useColor from '@terrazzo/use-color';
3 |
4 | export default {
5 | title: 'Components/Display/TrueGradient',
6 | component: TrueGradient,
7 | parameters: {
8 | layout: 'centered',
9 | },
10 | };
11 |
12 | export const Overview = {
13 | args: {
14 | start: 'color(srgb 1 0 0)',
15 | end: 'color(srgb 0 1 0)',
16 | },
17 | render(args) {
18 | const [start] = useColor(args.start);
19 | const [end] = useColor(args.end);
20 |
21 | return (
22 |
23 | Good
24 |
25 | Bad
26 |
33 |
34 | );
35 | },
36 | };
37 |
--------------------------------------------------------------------------------
/packages/storybook/src/components/StickerSheet.module.css:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | border-collapse: collapse;
3 | }
4 |
5 | .td {
6 | padding: var(--tz-space-300) var(--tz-space-200);
7 | text-align: center;
8 | }
9 |
10 | .th {
11 | color: var(--tz-text-secondary);
12 | font-family: var(--tz-font-mono);
13 | font-size: var(--tz-font-label-small-font-size);
14 | font-weight: var(--tz-font-label-small-font-weight);
15 | letter-spacing: var(--tz-font-label-small-letter-spacing);
16 | line-height: var(--tz-font-label-small-line-height);
17 | padding: var(--tz-space-100) var(--tz-space-200);
18 |
19 | &[scope="row"] {
20 | text-align: left;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/packages/storybook/src/components/StickerSheet.tsx:
--------------------------------------------------------------------------------
1 | import type { ReactElement } from 'react';
2 | import c from './StickerSheet.module.css';
3 |
4 | export interface StickerSheetProps {
5 | columns: string[];
6 | rows: string[];
7 | variants: { title: string; component: ReactElement }[];
8 | }
9 |
10 | export default function StickerSheet({ columns, rows, variants }: StickerSheetProps) {
11 | return (
12 |
13 |
14 |
15 |
16 | {columns.map((col) => (
17 |
18 | {col}
19 |
20 | ))}
21 |
22 |
23 |
24 | {rows.map((row, i) => {
25 | const start = i * columns.length;
26 | const rowVariants = variants.slice(start, start + columns.length);
27 | return (
28 |
29 |
30 | {row}
31 |
32 | {rowVariants.map((variant, i) => (
33 |
34 | {variant}
35 |
36 | ))}
37 |
38 | );
39 | })}
40 |
41 |
42 | );
43 | }
44 |
--------------------------------------------------------------------------------
/packages/storybook/stylelint.config.js:
--------------------------------------------------------------------------------
1 | import base from '../../stylelint.config.js';
2 |
3 | /** @type {import('stylelint').Config} */
4 | export default {
5 | ...base,
6 | };
7 |
--------------------------------------------------------------------------------
/packages/storybook/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.react.json",
3 | "compilerOptions": {
4 | "baseUrl": "src",
5 | "noEmit": true
6 | },
7 | "include": ["./src/"]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/storybook/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from '@vitejs/plugin-react-swc';
2 | import { defineConfig } from 'vite';
3 |
4 | export default defineConfig({
5 | plugins: [react()],
6 | });
7 |
--------------------------------------------------------------------------------
/packages/tiles/.npmignore:
--------------------------------------------------------------------------------
1 | *.test.*
2 | .size-limit.js
3 | .turbo
4 | biome.*
5 | rollup.*
6 | stylelint.*
7 | src/**
8 | tsconfig.*
9 | vite.*
10 | vitest.*
11 |
--------------------------------------------------------------------------------
/packages/tiles/.size-limit.js:
--------------------------------------------------------------------------------
1 | export default [
2 | { ignore: ['@use-gesture/react', 'culori', 'react', 'shiki'], path: './dist/index.js', limit: '35 kB' },
3 | ];
4 |
--------------------------------------------------------------------------------
/packages/tiles/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @terrazzo/tiles
2 |
3 | ## 0.0.7
4 |
5 | ### Patch Changes
6 |
7 | - Bugfixes thanks to [@lilnasy](lilnasy)
8 |
9 | ## 0.0.3
10 |
11 | ### Patch Changes
12 |
13 | - [#2](https://github.com/terrazzoapp/tiles/pull/2) [`0a7e761`](https://github.com/terrazzoapp/tiles/commit/0a7e7617b820b25c421bef71c2d6d832fa803729) Thanks [@drwpow](https://github.com/drwpow)! - Allow buttons to be links
14 |
15 | ## 0.0.2
16 |
17 | ### Patch Changes
18 |
19 | - [#1](https://github.com/terrazzoapp/tiles/pull/1) [`1edb094`](https://github.com/terrazzoapp/tiles/commit/1edb094f01b17f5b43728ba4fb1b15d7b10fa5a9) Thanks [@drwpow](https://github.com/drwpow)! - Fix Svelte package, export types
20 |
21 | ## 0.0.1
22 |
23 | ### Patch Changes
24 |
25 | - [`934d708`](https://github.com/terrazzoapp/tiles/commit/934d7083768bc60bc21d0ac206f184cf9a91ecf4) Thanks [@drwpow](https://github.com/drwpow)! - Add light and dark mode CSS
26 |
27 | - [`934d708`](https://github.com/terrazzoapp/tiles/commit/934d7083768bc60bc21d0ac206f184cf9a91ecf4) Thanks [@drwpow](https://github.com/drwpow)! - Add button colors, improve CodeBlock copy button
28 |
29 | ## 0.0.0
30 |
31 | Initial Release
32 |
--------------------------------------------------------------------------------
/packages/tiles/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Drew Powers
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/tiles/README.md:
--------------------------------------------------------------------------------
1 | # @terrazzo/tiles
2 |
3 | Terrazzo’s component library. Sizes enforced by [size-limit](https://www.npmjs.com/package/size-limit)
4 |
5 | ## Setup
6 |
7 | ```sh
8 | npm i @terrazzo/tiles
9 | ```
10 |
11 | ```tsx
12 | import { Button, Select, SubtleInput, Switch, TokenType, TrueGradient } from '@terrazzo/tiles';
13 | ```
14 |
--------------------------------------------------------------------------------
/packages/tiles/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
3 | "extends": ["../../biome.json"],
4 | "linter": {
5 | "rules": {
6 | "a11y": {
7 | "useFocusableInteractive": "off",
8 | "noNoninteractiveTabindex": "off"
9 | }
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/packages/tiles/rollup.config.js:
--------------------------------------------------------------------------------
1 | import ts from '@rollup/plugin-typescript';
2 | import css from 'rollup-plugin-import-css';
3 |
4 | /** @type {import("rollup").InputOptions} */
5 | export default {
6 | plugins: [
7 | css({
8 | output: 'all-components.css',
9 | }),
10 | ts({
11 | tsconfig: './tsconfig.build.json',
12 | }),
13 | ],
14 | input: 'src/index.ts',
15 | external: ['*'],
16 | output: {
17 | dir: './dist/',
18 | sourcemap: true,
19 | globals: {
20 | 'react/jsx-runtime': 'jsxRuntime',
21 | 'react-dom/client': 'ReactDOM',
22 | react: 'React',
23 | },
24 | },
25 | };
26 |
--------------------------------------------------------------------------------
/packages/tiles/src/Button/Button.tsx:
--------------------------------------------------------------------------------
1 | import clsx from 'clsx';
2 | import type { ComponentProps } from 'react';
3 | import './Button.css';
4 |
5 | export interface ButtonProps extends Omit, 'size'> {
6 | /** default: "m" */
7 | size?: 's' | 'm';
8 | /** default: "primary" */
9 | variant?: 'lime' | 'blue' | 'orange' | 'secondary';
10 | }
11 |
12 | export default function Button({
13 | className,
14 | children,
15 | size = 'm',
16 | type = 'button',
17 | variant = 'secondary',
18 | ref,
19 | ...rest
20 | }: ButtonProps) {
21 | return (
22 |
30 | {children}
31 |
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/packages/tiles/src/ButtonLink/ButtonLink.tsx:
--------------------------------------------------------------------------------
1 | import clsx from 'clsx';
2 | import type { ComponentProps } from 'react';
3 | import '../Button/Button.css'; // note: a component should NEVER import another’s styles, but this is a special case
4 |
5 | export interface ButtonLinkProps extends ComponentProps<'a'> {
6 | /** default: "m" */
7 | size?: 's' | 'm';
8 | /** default: "primary" */
9 | variant?: 'lime' | 'blue' | 'orange' | 'secondary' | 'tertiary';
10 | }
11 |
12 | export default function ButtonLink({
13 | className,
14 | children,
15 | size = 'm',
16 | variant = 'secondary',
17 | ref,
18 | ...rest
19 | }: ButtonLinkProps) {
20 | return (
21 |
22 | {children}
23 |
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/packages/tiles/src/CopyButton/CopyButton.css:
--------------------------------------------------------------------------------
1 | .tz-copy-button {
2 | --tz-copy-btn-size: 2rem;
3 |
4 | align-items: center;
5 | background-color: transparent;
6 | border: 1px solid var(--tz-border-1);
7 | border-radius: var(--tz-radius-300);
8 | color: var(--tz-icon-secondary);
9 | cursor: pointer;
10 | display: inline-flex;
11 | height: var(--tz-space-control-m);
12 | justify-content: center;
13 | min-width: var(--tz-space-control-m);
14 | outline: 2px solid transparent;
15 | padding: 0;
16 | transition: outline-color var(--tz-timing-default) var(--tz-ease-linear);
17 | width: var(--tz-space-control-m);
18 | z-index: 2;
19 |
20 | &:focus-visible {
21 | outline-color: var(--tz-nav-primary-highlight);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/packages/tiles/src/CopyButton/CopyButton.tsx:
--------------------------------------------------------------------------------
1 | import { Check, Copy } from '@terrazzo/icons';
2 | import clsx from 'clsx';
3 | import { type ComponentProps, useRef, useState } from 'react';
4 | import './CopyButton.css';
5 |
6 | export interface CopyButtonProps extends Omit, 'children' | 'onClick'> {
7 | /** The text to copy to the clipboard */
8 | clipboardText: string;
9 | /**
10 | * Amount of time, in milliseconds, to show the checkmark.
11 | * @default: 1000
12 | */
13 | timeoutMS?: number;
14 | }
15 |
16 | export default function CopyButton({ className, clipboardText, timeoutMS = 1000, ...rest }: CopyButtonProps) {
17 | const [copied, setCopied] = useState(false);
18 | const copiedTO = useRef(undefined);
19 |
20 | return (
21 | {
25 | await navigator.clipboard.writeText(clipboardText);
26 | clearTimeout(copiedTO.current);
27 | setCopied(true);
28 | copiedTO.current = window.setTimeout(() => {
29 | setCopied(false);
30 | }, timeoutMS);
31 | }}
32 | {...rest}
33 | >
34 | {copied ? : }
35 |
36 | );
37 | }
38 |
--------------------------------------------------------------------------------
/packages/tiles/src/Demo/Demo.css:
--------------------------------------------------------------------------------
1 | .tz-demo {
2 | display: grid;
3 | }
4 |
5 | .tz-demo-canvas {
6 | padding: var(--tz-space-400);
7 | }
8 |
9 | .tz-demo-code {
10 | contain: inline-size;
11 | }
12 |
13 | .tz-demo-code-copy-wrapper {
14 | margin: 0;
15 | padding: 0;
16 | position: absolute;
17 | right: var(--tz-space-300);
18 | top: var(--tz-space-200);
19 | z-index: var(--tz-layer-nav);
20 | }
21 |
22 | .tz-demo-code-icon {
23 | opacity: 0.75;
24 | }
25 |
26 | .tz-demo-code-overflow {
27 | overflow-x: auto;
28 | }
29 |
--------------------------------------------------------------------------------
/packages/tiles/src/Demo/Demo.tsx:
--------------------------------------------------------------------------------
1 | import { type ComponentProps, useEffect, useState } from 'react';
2 | import type { CodeOptionsSingleTheme, CodeToHastOptions } from 'shiki';
3 | import './Demo.css';
4 | import CopyButton from '../CopyButton/CopyButton.js';
5 |
6 | const shiki = import('shiki');
7 |
8 | export interface DemoProps extends Omit, 'lang'> {
9 | /** Code to Highlight */
10 | code: string;
11 | lang?: CodeToHastOptions['lang'];
12 | theme?: CodeOptionsSingleTheme['theme'];
13 | }
14 |
15 | export default function Demo({ children, code, lang = 'tsx', theme = 'houston', ...rest }: DemoProps) {
16 | const [codeFormatted, setCodeFormatted] = useState('');
17 |
18 | useEffect(() => {
19 | shiki.then(async ({ codeToHtml }) => {
20 | const formatted = await codeToHtml(code, { lang, theme });
21 | setCodeFormatted(formatted);
22 | });
23 | }, [code, lang, theme]);
24 |
25 | return (
26 |
27 |
{children}
28 |
38 |
39 | );
40 | }
41 |
--------------------------------------------------------------------------------
/packages/tiles/src/Fieldset/Fieldset.css:
--------------------------------------------------------------------------------
1 | .tz-fieldset {
2 | border: none;
3 | display: flex;
4 | flex-direction: column;
5 | gap: var(--tz-space-300);
6 | padding: 0;
7 | }
8 |
9 | .tz-fieldset-legend {
10 | color: var(--tz-text-secondary);
11 | display: flex;
12 | font-family: var(--tz-font-sans);
13 | font-size: var(--tz-font-body-font-size);
14 | letter-spacing: var(--tz-font-body-letter-spacing);
15 | line-height: var(--tz-font-body-line-height);
16 | margin-bottom: var(--tz-space-300);
17 | position: static;
18 | width: 100%;
19 | }
20 |
--------------------------------------------------------------------------------
/packages/tiles/src/Fieldset/Fieldset.tsx:
--------------------------------------------------------------------------------
1 | import clsx from 'clsx';
2 | import type { ComponentProps, ReactNode } from 'react';
3 | import './Fieldset.css';
4 |
5 | export interface FieldsetProps extends ComponentProps<'fieldset'> {
6 | label: ReactNode;
7 | }
8 |
9 | export default function Fieldset({ children, className, label, ref, ...rest }: FieldsetProps) {
10 | return (
11 |
12 | {label}
13 | {children}
14 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/packages/tiles/src/Kbd/Kbd.css:
--------------------------------------------------------------------------------
1 | .tz-kbd {
2 | align-items: center;
3 | background-color: var(--tz-color-content-bg);
4 | border: 1px solid var(--tz-color-content-border);
5 | border-radius: var(--tz-radius-200);
6 | color: var(--tz-text-primary);
7 | cursor: default;
8 | display: inline-flex;
9 | font-family: var(--tz-font-mono);
10 | font-size: inherit;
11 | font-weight: var(--tz-font-body-font-weight);
12 | height: 1.5em;
13 | justify-content: center;
14 | letter-spacing: var(--tz-font-body-letter-spacing);
15 | line-height: inherit;
16 | padding: 0;
17 | user-select: none;
18 | width: 1.5em;
19 | }
20 |
--------------------------------------------------------------------------------
/packages/tiles/src/Kbd/Kbd.tsx:
--------------------------------------------------------------------------------
1 | import clsx from 'clsx';
2 | import type { ComponentProps } from 'react';
3 | import './Kbd.css';
4 |
5 | export type KbdProps = ComponentProps<'kbd'>;
6 |
7 | export default function Kbd({ className, children, ...rest }: KbdProps) {
8 | return (
9 |
10 | {children}
11 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/packages/tiles/src/SubtleInput/SubtleInput.tsx:
--------------------------------------------------------------------------------
1 | import clsx from 'clsx';
2 | import type { ComponentProps } from 'react';
3 | import './SubtleInput.css';
4 |
5 | const INPUT_MODE: Record['inputMode']> = {
6 | text: 'text',
7 | number: 'decimal',
8 | email: 'email',
9 | search: 'search',
10 | tel: 'tel',
11 | url: 'url',
12 | };
13 |
14 | export interface SubtleInputProps extends ComponentProps<'input'> {
15 | type: 'text' | 'number' | 'email' | 'search' | 'tel' | 'url';
16 | suffix?: string;
17 | }
18 |
19 | export default function SubtleInput({ className, suffix, ref, ...rest }: SubtleInputProps) {
20 | return (
21 |
22 |
23 | {suffix}
24 |
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/packages/tiles/src/Switch/Switch.tsx:
--------------------------------------------------------------------------------
1 | import clsx from 'clsx';
2 | import { type ComponentProps, type ReactNode, useId } from 'react';
3 | import './Switch.css';
4 |
5 | export interface SwitchProps extends ComponentProps<'input'> {
6 | /** Accessible label for this toggle switch */
7 | label: ReactNode;
8 | }
9 |
10 | export default function Switch({ className, id: userID, label, ref, ...rest }: SwitchProps) {
11 | const randomID = useId();
12 | const id = userID ?? randomID;
13 |
14 | return (
15 |
16 |
25 |
26 | {label}
27 |
28 |
29 | );
30 | }
31 |
--------------------------------------------------------------------------------
/packages/tiles/src/TokenType/TokenType.css:
--------------------------------------------------------------------------------
1 | .tz-tokentype {
2 | align-items: center;
3 | display: flex;
4 | font-family: var(--tz-font-sans);
5 | font-size: var(--tz-font-body-font-size);
6 | font-weight: var(--tz-font-body-font-weight);
7 | gap: var(--tz-space-300);
8 | letter-spacing: var(--tz-font-body-letter-spacing);
9 | line-height: 1;
10 | }
11 |
12 | .tz-tokentype-icon {
13 | color: var(--tz-icon-secondary);
14 | height: 1em;
15 | width: 1em;
16 | }
17 |
--------------------------------------------------------------------------------
/packages/tiles/src/Tooltip/Tooltip.css:
--------------------------------------------------------------------------------
1 | .tz-tooltip-content {
2 | animation-duration: 400ms;
3 | background: var(--tz-color-content-bg-elevated);
4 | border: 1px solid var(--tz-color-content-border-elevated);
5 | border-radius: var(--tz-radius-2);
6 | box-shadow: var(--tz-shadow-300);
7 | color: var(--tz-text-secondary);
8 | font-family: var(--tz-font-sans);
9 | font-size: var(--tz-font-body-font-size);
10 | font-weight: var(--tz-font-body-font-weight);
11 | letter-spacing: var(--tz-font-body-letter-spacing);
12 | line-height: var(--tz-font-body-line-height);
13 | padding: var(--tz-space-300);
14 | user-select: none;
15 | will-change: transform, opacity;
16 | z-index: var(--tz-layer-popover);
17 |
18 | a {
19 | color: var(--tz-text-link-primary);
20 | }
21 | }
22 |
23 | .tz-tooltip-arrow {
24 | fill: var(--tz-color-content-bg-elevated);
25 | }
26 |
--------------------------------------------------------------------------------
/packages/tiles/src/TreeGrid/Item.tsx:
--------------------------------------------------------------------------------
1 | import clsx from 'clsx';
2 | import { type ComponentProps, type ReactNode, useContext, useEffect } from 'react';
3 | import { NestedContext } from './Group.js';
4 | import { Context } from './Root.js';
5 |
6 | export interface ItemProps extends ComponentProps<'tr'> {
7 | /**
8 | * Unique ID for this item
9 | * ⚠️ must not conflict with any other items, or any parent groups!
10 | */
11 | id: string;
12 | actions?: ReactNode;
13 | hidden?: boolean;
14 | }
15 |
16 | export default function Item({ actions, children, className, hidden, id, ...rest }: ItemProps) {
17 | const { selected, registerID } = useContext(Context);
18 | const { level, isParentExpanded, parentID } = useContext(NestedContext);
19 |
20 | useEffect(() => {
21 | registerID(parentID, id);
22 | }, [parentID, id]);
23 |
24 | return (
25 |
34 |
35 | {children}
36 |
37 | {actions}
38 |
39 | );
40 | }
41 |
--------------------------------------------------------------------------------
/packages/tiles/src/TreeGrid/index.tsx:
--------------------------------------------------------------------------------
1 | import './TreeGrid.css';
2 |
3 | export * from './Group.js';
4 | export { default as Group } from './Group.js';
5 | export * from './Item.js';
6 | export { default as Item } from './Item.js';
7 | export * from './Root.js';
8 | export { default as Root } from './Root.js';
9 |
--------------------------------------------------------------------------------
/packages/tiles/src/hooks/resize-observer.ts:
--------------------------------------------------------------------------------
1 | import { type MutableRefObject, useEffect, useState } from 'react';
2 |
3 | /** Use performant Resize Observer */
4 | export default function useResizeObserver>(el: T): DOMRect {
5 | const [domRect, setDomRect] = useState({
6 | width: 0,
7 | height: 0,
8 | x: 0,
9 | y: 0,
10 | left: 0,
11 | top: 0,
12 | right: 0,
13 | bottom: 0,
14 | } as DOMRect);
15 | useEffect(() => {
16 | if (!el.current) {
17 | return;
18 | }
19 | const ro = new ResizeObserver(([entry]) => {
20 | if (entry) {
21 | setDomRect(entry.contentRect);
22 | }
23 | });
24 | ro.observe(el.current);
25 | return () => {
26 | ro.disconnect();
27 | };
28 | }, [el.current]);
29 | return domRect;
30 | }
31 |
--------------------------------------------------------------------------------
/packages/tiles/src/lib/number.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, expect, it } from 'vitest';
2 | import { clamp, snap } from './number.js';
3 |
4 | describe('clamp', () => {
5 | it('basic', () => {
6 | expect(clamp(0.5, 0, 1)).toBe(0.5);
7 | expect(clamp(1.5, 0, 1)).toBe(1);
8 | expect(clamp(-1, 0, 1)).toBe(0);
9 | });
10 | });
11 |
12 | describe('snap', () => {
13 | it('basic', () => {
14 | expect(snap(0.5, 0.1)).toBe(0.5);
15 | expect(snap(342, 1)).toBe(342);
16 | expect(snap(342.000001, 0.01)).toBe(342);
17 | expect(snap(0.5, 1)).toBe(1);
18 | expect(snap(0.1 + 0.2, 0.1)).toBe(0.3);
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/packages/tiles/src/lib/number.ts:
--------------------------------------------------------------------------------
1 | /** Clamp a number between 2 values */
2 | export function clamp(value: number, min: number, max: number) {
3 | if (typeof value !== 'number') {
4 | return value;
5 | }
6 | return Math.min(Math.max(value, min), max);
7 | }
8 |
9 | /** Snap a number to n decimal places */
10 | export function snap(value: number, precision: number) {
11 | if (typeof value !== 'number' || precision <= 0 || precision > 1 || value % 1 === 0) {
12 | return value;
13 | }
14 | const p = 1 / precision;
15 | return Math.round(value * p) / p;
16 | }
17 |
--------------------------------------------------------------------------------
/packages/tiles/src/lib/set.ts:
--------------------------------------------------------------------------------
1 | export function addToSet(newValue: T) {
2 | return (current: Set) => new Set([...current, newValue]);
3 | }
4 |
5 | export function removeFromSet(oldValue: T) {
6 | return (current: Set) => new Set([...current].filter((v) => v !== oldValue));
7 | }
8 |
--------------------------------------------------------------------------------
/packages/tiles/src/types.d.ts:
--------------------------------------------------------------------------------
1 | import 'react';
2 |
3 | declare module 'react' {
4 | interface CSSProperties {
5 | [key: `--${string}`]: string | number | undefined;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/tiles/stylelint.config.js:
--------------------------------------------------------------------------------
1 | import base from '../../stylelint.config.js';
2 |
3 | /** @type {import('stylelint').Config} */
4 | export default {
5 | ...base,
6 | };
7 |
--------------------------------------------------------------------------------
/packages/tiles/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "module": "NodeNext",
5 | "moduleResolution": "nodenext"
6 | },
7 | "exclude": ["**/__test__/**", "**/*.test.*"]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/tiles/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.react.json",
3 | "compilerOptions": {
4 | "baseUrl": "src",
5 | "outDir": "dist"
6 | },
7 | "include": ["./src/"]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/tiles/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from '@vitejs/plugin-react-swc';
2 | import { defineConfig } from 'vitest/config';
3 |
4 | /** @see https://vitejs.dev/config/ */
5 | export default defineConfig({
6 | plugins: [react({ devTarget: 'esnext' })],
7 | test: {
8 | environment: 'jsdom',
9 | restoreMocks: true,
10 | },
11 | });
12 |
--------------------------------------------------------------------------------
/packages/token-lab/.gitignore:
--------------------------------------------------------------------------------
1 | public/monaco-editor/
2 |
--------------------------------------------------------------------------------
/packages/token-lab/.npmignore:
--------------------------------------------------------------------------------
1 | *.test.*
2 | .size-limit.js
3 | .turbo
4 | biome.*
5 | rollup.*
6 | stylelint.*
7 | src/**
8 | tsconfig.*
9 | vite.*
10 | vitest.*
11 |
--------------------------------------------------------------------------------
/packages/token-lab/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @terrazzo/token-lab
2 |
--------------------------------------------------------------------------------
/packages/token-lab/README.md:
--------------------------------------------------------------------------------
1 | # @terrazzo/token-lab
2 |
3 | Tokens.json editor and viewer.
4 |
5 | ## Setup
6 |
7 | ```sh
8 | pnpm
9 | ```
10 |
11 | ```tsx
12 | import TokenLab from '@terrazzo/token-lab';
13 | import tokens from './tokens.json' with { type: 'json' };
14 |
15 | ;
18 | ```
19 |
--------------------------------------------------------------------------------
/packages/token-lab/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
3 | "extends": ["../../biome.json"],
4 | "linter": {
5 | "rules": {
6 | "a11y": {
7 | "useSemanticElements": "off"
8 | },
9 | "suspicious": {
10 | "noArrayIndexKey": "off"
11 | }
12 | }
13 | },
14 | "css": {
15 | "parser": {
16 | "cssModules": true
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/packages/token-lab/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 | Token Lab: build, edit, and customize DTCG tokens.json
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/packages/token-lab/public/assets/terrazzo-logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/packages/token-lab/public/favicon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/token-lab/src/app.tsx:
--------------------------------------------------------------------------------
1 | import './styles/tokens.css';
2 | import '@terrazzo/fonts/fragment-mono.css';
3 | import '@terrazzo/fonts/instrument-sans.css';
4 | import '@terrazzo/tiles/dist/all-components.css';
5 | import '@terrazzo/react-color-picker/dist/all-components.css';
6 | import './styles/global.css';
7 | import { Provider as JotaiProvider } from 'jotai';
8 | import { TokensFileContext } from './hooks/tokens.js';
9 | import { DefaultLayout } from './layouts/Default/Default.js';
10 |
11 | export default function App({ tokensFile, onUpdate }: { tokensFile?: string; onUpdate?: (file: string) => unknown }) {
12 | return (
13 |
14 |
15 |
16 |
17 |
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/packages/token-lab/src/components/CodePanel/CodePanel.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | position: relative;
3 | z-index: 10;
4 | }
5 |
6 | .btn {
7 | align-items: center;
8 | background: none;
9 | border: none;
10 | color: inherit;
11 | cursor: pointer;
12 | display: flex;
13 | font-size: 1rem;
14 | height: 3rem;
15 | justify-content: center;
16 | width: 3rem;
17 |
18 | svg {
19 | height: 1.25rem;
20 | width: 1.25rem;
21 | }
22 | }
23 |
24 | .btnClose {
25 | height: 2rem;
26 | width: 2rem;
27 |
28 | svg {
29 | height: 1rem;
30 | width: 1rem;
31 | }
32 | }
33 |
34 | .panel {
35 | background-color: #202020;
36 | border: var(--tz-border-2);
37 | bottom: 0;
38 | display: grid;
39 | grid-template-rows: 2rem auto;
40 | height: 100vh;
41 | position: fixed;
42 | right: 0;
43 | top: 0;
44 | width: 40rem;
45 |
46 | > * {
47 | height: 100%;
48 | width: 100%;
49 | }
50 |
51 | &[hidden] {
52 | display: none;
53 | }
54 | }
55 |
56 | .menu {
57 | align-items: center;
58 | background-color: var(--tz-color-bg-2);
59 | border-bottom: var(--tz-border-2);
60 | display: flex;
61 | justify-content: space-between;
62 | }
63 |
64 | .title {
65 | align-items: center;
66 | display: inline-flex;
67 | font-family: var(--tz-monospace);
68 | font-size: 0.75rem;
69 | margin-right: auto;
70 | padding-inline: 0.5rem;
71 | }
72 |
--------------------------------------------------------------------------------
/packages/token-lab/src/components/CodePanel/CodePanel.module.d.css.ts:
--------------------------------------------------------------------------------
1 | declare const classNames: {
2 | readonly container: 'container';
3 | readonly btn: 'btn';
4 | readonly btnClose: 'btnClose';
5 | readonly panel: 'panel';
6 | readonly menu: 'menu';
7 | readonly title: 'title';
8 | };
9 | export default classNames;
10 |
--------------------------------------------------------------------------------
/packages/token-lab/src/components/EditableColorToken/EditableColorToken.module.d.css.ts:
--------------------------------------------------------------------------------
1 | declare const classNames: {
2 | readonly container: 'container';
3 | readonly swatch: 'swatch';
4 | readonly popover: 'popover';
5 | readonly components: 'components';
6 | readonly colorSpaceDropdown: 'colorSpaceDropdown';
7 | readonly 'tz-select-item-inner': 'tz-select-item-inner';
8 | readonly 'tz-select-item-icon': 'tz-select-item-icon';
9 | readonly colorSpaceDropdownItem: 'colorSpaceDropdownItem';
10 | readonly channel: 'channel';
11 | readonly alpha: 'alpha';
12 | };
13 | export default classNames;
14 |
--------------------------------------------------------------------------------
/packages/token-lab/src/components/EditableToken/EditableToken.module.css:
--------------------------------------------------------------------------------
1 | .cell {
2 | border-bottom: var(--tz-border-3);
3 | border-right: var(--tz-border-3);
4 | font-family: var(--tz-font-sans);
5 | font-size: var(--tz-font-body-font-size);
6 | font-variation-settings: var(--tz-font-body-font-variation-settings);
7 | font-weight: var(--tz-font-body-font-weight);
8 | letter-spacing: var(--tz-font-body-letter-spacing);
9 | line-height: var(--tz-font-body-line-height);
10 | padding: 0.5rem;
11 | }
12 |
13 | .code {
14 | font-family: var(--tz-font-mono);
15 | font-size: 0.75rem;
16 | }
17 |
--------------------------------------------------------------------------------
/packages/token-lab/src/components/EditableToken/EditableToken.module.d.css.ts:
--------------------------------------------------------------------------------
1 | declare const classNames: {
2 | readonly cell: 'cell';
3 | readonly code: 'code';
4 | };
5 | export default classNames;
6 |
--------------------------------------------------------------------------------
/packages/token-lab/src/components/EditableToken/EditableToken.tsx:
--------------------------------------------------------------------------------
1 | import type { TokenNormalized } from '@terrazzo/token-tools';
2 | import clsx from 'clsx';
3 | import EditableColorToken from '../EditableColorToken/EditableColorToken.js';
4 | import c from './EditableToken.module.css';
5 |
6 | export interface TokenProps {
7 | token: TokenNormalized;
8 | /** ordered listing of modes ("." is always first, even if omitted) */
9 | modes?: string[];
10 | }
11 |
12 | export default function EditableToken({ token, modes = ['.'] }: TokenProps) {
13 | const localName = token.id.replace(`${token.group.id}.`, '');
14 |
15 | switch (token.$type) {
16 | case 'color': {
17 | return (
18 |
19 |
20 | {localName}
21 |
22 | {modes.map((mode) => (
23 |
24 |
25 |
26 | ))}
27 |
28 | );
29 | }
30 | default: {
31 | return (
32 |
33 |
34 | {localName}
35 |
36 | {modes.map((mode) => (
37 |
38 | {mode in token.mode && JSON.stringify(token.mode[mode])}
39 |
40 | ))}
41 |
42 | );
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/packages/token-lab/src/components/MainNav/MainNav.module.css:
--------------------------------------------------------------------------------
1 | .header {
2 | align-items: center;
3 | border-bottom: var(--tz-border-3);
4 | display: flex;
5 | height: var(--main-nav-height);
6 | user-select: none;
7 | }
8 |
9 | .logo {
10 | height: 1.25rem;
11 | margin-inline-start: 1rem;
12 | width: 1.25rem;
13 | }
14 |
15 | .codePanel {
16 | margin-inline-start: auto;
17 | }
18 |
19 | .title {
20 | font-size: 1rem;
21 | font-weight: 800;
22 | line-height: 1;
23 | margin: 0;
24 | padding-inline-start: 0.375rem;
25 | }
26 |
--------------------------------------------------------------------------------
/packages/token-lab/src/components/MainNav/MainNav.module.d.css.ts:
--------------------------------------------------------------------------------
1 | declare const classNames: {
2 | readonly header: 'header';
3 | readonly logo: 'logo';
4 | readonly codePanel: 'codePanel';
5 | readonly title: 'title';
6 | };
7 | export default classNames;
8 |
--------------------------------------------------------------------------------
/packages/token-lab/src/components/TokensEditor/TokensEditor.module.css:
--------------------------------------------------------------------------------
1 | .multipleGroups {
2 | padding: 1.5rem;
3 | }
4 |
5 | .group {
6 | margin-block: 1.5rem;
7 | padding-inline: 1.5rem;
8 | }
9 |
10 | .groupTitle {
11 | font-family: var(--tz-font-sans);
12 | font-size: var(--tz-font-heading4-font-size);
13 | font-variation-settings: var(--tz-font-heading4-font-variation-settings);
14 | font-weight: var(--tz-font-heading4-font-weight);
15 | letter-spacing: var(--tz-font-heading4-letter-spacing);
16 | line-height: var(--tz-font-heading4-line-height);
17 | }
18 |
19 | .tokenGrid {
20 | border-collapse: collapse;
21 | border-left: var(--tz-border-3);
22 | border-top: var(--tz-border-3);
23 | text-align: left;
24 | width: 100%;
25 | }
26 |
27 | .colheader {
28 | border-bottom: var(--tz-border-3);
29 | border-right: var(--tz-border-3);
30 | font-family: var(--tz-font-sans);
31 | font-size: var(--tz-font-body-strong-font-size);
32 | font-variation-settings: var(--tz-font-body-strong-font-variation-settings);
33 | font-weight: var(--tz-font-body-strong-font-weight);
34 | letter-spacing: var(--tz-font-body-strong-letter-spacing);
35 | line-height: var(--tz-font-body-strong-line-height);
36 | padding: 0.5rem;
37 | }
38 |
--------------------------------------------------------------------------------
/packages/token-lab/src/components/TokensEditor/TokensEditor.module.d.css.ts:
--------------------------------------------------------------------------------
1 | declare const classNames: {
2 | readonly multipleGroups: 'multipleGroups';
3 | readonly group: 'group';
4 | readonly groupTitle: 'groupTitle';
5 | readonly tokenGrid: 'tokenGrid';
6 | readonly colheader: 'colheader';
7 | };
8 | export default classNames;
9 |
--------------------------------------------------------------------------------
/packages/token-lab/src/components/TokensNav/TokensNav.module.d.css.ts:
--------------------------------------------------------------------------------
1 | declare const classNames: {
2 | readonly container: 'container';
3 | readonly tree: 'tree';
4 | readonly error: 'error';
5 | readonly search: 'search';
6 | readonly searchIcon: 'searchIcon';
7 | readonly tokenIcon: 'tokenIcon';
8 | };
9 | export default classNames;
10 |
--------------------------------------------------------------------------------
/packages/token-lab/src/hooks/navigation.ts:
--------------------------------------------------------------------------------
1 | import { atom, useAtom } from 'jotai';
2 | import { useEffect } from 'react';
3 |
4 | export interface NavState {
5 | selection: string[];
6 | }
7 |
8 | /**
9 | * Naviation
10 | * The simple version of this app only uses a single route with clientside search params, so we can
11 | * implement routing using a couple Jotai atoms without the need for a heavier router.
12 | */
13 | export default function useNavigation() {
14 | const [state, setState] = useAtom($state);
15 |
16 | // keep search params synced with state
17 | useEffect(() => {
18 | const params = new URLSearchParams(window.location.search);
19 | params.set('selected', serializeSelected(state.selection));
20 | window.history.replaceState({}, '', `${window.location.pathname}?${params}`);
21 | }, [state]);
22 |
23 | return [state, setState] as const;
24 | }
25 |
26 | function serializeSelected(selected: string[]) {
27 | return selected.join(',');
28 | }
29 |
30 | function deserializeSelected(selected: string) {
31 | return selected.split(',');
32 | }
33 |
34 | const rawParams = new URLSearchParams(typeof window !== 'undefined' ? window.location.search : '');
35 | const parsed: NavState = {
36 | selection: deserializeSelected(rawParams.get('selected') || ''),
37 | };
38 |
39 | const $state = atom(parsed);
40 |
--------------------------------------------------------------------------------
/packages/token-lab/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { createRoot } from 'react-dom/client';
2 | import App from './app.js';
3 |
4 | const root = createRoot(document.getElementById('app')!);
5 | root.render( );
6 |
--------------------------------------------------------------------------------
/packages/token-lab/src/layouts/Default/Default.module.css:
--------------------------------------------------------------------------------
1 | .layout {
2 | --main-nav-height: 3rem;
3 | --sidebar-width: 16rem;
4 |
5 | display: grid;
6 | grid-template-rows: var(--main-nav-height) auto;
7 | height: 100svh;
8 | height: 100vh;
9 | overflow: hidden;
10 | width: 100svw;
11 | width: 100vw;
12 | }
13 |
14 | .page {
15 | display: grid;
16 | grid-template-columns: var(--sidebar-width) auto;
17 | }
18 |
19 | .sidebar {
20 | display: flex;
21 | flex-direction: column;
22 | height: 100%;
23 | max-height: calc(100svh - var(--main-nav-height));
24 | max-height: calc(100vh - var(--main-nav-height));
25 | }
26 |
27 | .main {
28 | max-height: calc(100svh - var(--main-nav-height));
29 | max-height: calc(100vh - var(--main-nav-height));
30 | overflow-y: auto;
31 | }
32 |
--------------------------------------------------------------------------------
/packages/token-lab/src/layouts/Default/Default.module.d.css.ts:
--------------------------------------------------------------------------------
1 | declare const classNames: {
2 | readonly layout: 'layout';
3 | readonly page: 'page';
4 | readonly sidebar: 'sidebar';
5 | readonly main: 'main';
6 | };
7 | export default classNames;
8 |
--------------------------------------------------------------------------------
/packages/token-lab/src/layouts/Default/Default.tsx:
--------------------------------------------------------------------------------
1 | import MainNav from '../../components/MainNav/MainNav.js';
2 | import TokensEditor from '../../components/TokensEditor/TokensEditor.js';
3 | import TokensNav from '../../components/TokensNav/TokensNav.js';
4 | import c from './Default.module.css';
5 |
6 | export function DefaultLayout() {
7 | return (
8 |
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/packages/token-lab/src/lib/indexed-db.ts:
--------------------------------------------------------------------------------
1 | export async function getDB(
2 | name: string,
3 | {
4 | version = 1,
5 | onupgradeneeded,
6 | }: {
7 | version?: number;
8 | onupgradeneeded?: NonNullable;
9 | } = {},
10 | ): Promise {
11 | return await new Promise((resolve, reject) => {
12 | const dbReq = window.indexedDB.open(name, version);
13 | dbReq.onerror = (evt) => {
14 | reject(`IndexedDB not supported. Unable to save tokens!\nError: ${(evt.target as IDBOpenDBRequest).error}`);
15 | };
16 | if (onupgradeneeded) {
17 | dbReq.onupgradeneeded = onupgradeneeded;
18 | }
19 | dbReq.onsuccess = () => {
20 | resolve(dbReq!.result);
21 | };
22 | });
23 | }
24 |
--------------------------------------------------------------------------------
/packages/token-lab/src/styles/global.css:
--------------------------------------------------------------------------------
1 | html {
2 | height: 100%;
3 | }
4 |
5 | body {
6 | background-color: var(--tz-color-bg-1);
7 | color: var(--tz-color-text-1);
8 | font-family: var(--tz-font-sans);
9 | font-size: 100%;
10 | font-variant-numeric: tabular-nums;
11 | font-variation-settings: var(--tz-font-body-font-variation-settings);
12 | font-weight: var(--tz-font-body-font-weight);
13 | line-height: 1;
14 | margin: 0;
15 | }
16 |
17 | *,
18 | *::before,
19 | *::after {
20 | box-sizing: border-box;
21 | outline: 1px solid transparent;
22 |
23 | &:focus-visible {
24 | outline-color: var(--tz-color-base-lime-800);
25 | }
26 | }
27 |
28 | ::selection {
29 | background-color: var(--tz-color-base-lime-800);
30 | color: var(--tz-color-base-gray-100);
31 | }
32 |
33 | code,
34 | pre {
35 | font-family: var(--tz-font-monospace);
36 | }
37 |
--------------------------------------------------------------------------------
/packages/token-lab/src/types/react.d.ts:
--------------------------------------------------------------------------------
1 | import 'react';
2 |
3 | declare module 'react' {
4 | interface CSSProperties {
5 | [key: `--${string}`]: string | number | undefined;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/token-lab/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.react.json",
3 | "compilerOptions": {
4 | "baseUrl": "src",
5 | "outDir": "dist"
6 | },
7 | "include": ["./src/"]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/token-lab/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from '@vitejs/plugin-react-swc';
2 | import { defineConfig } from 'vite';
3 | import sassDts from 'vite-plugin-sass-dts';
4 |
5 | export default defineConfig({
6 | plugins: [react(), sassDts({ esmExport: true })],
7 | });
8 |
--------------------------------------------------------------------------------
/packages/token-tools/.npmignore:
--------------------------------------------------------------------------------
1 | .turbo
2 | biome.json
3 | src/**
4 | test/**
5 | tsconfig.*
6 | vitest.*
7 |
--------------------------------------------------------------------------------
/packages/token-tools/README.md:
--------------------------------------------------------------------------------
1 | # @terrazzo/token-tools
2 |
3 | ## Install
4 |
5 | ```sh
6 | npm install @terrazzo/token-tools
7 | ```
8 |
--------------------------------------------------------------------------------
/packages/token-tools/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
3 | "extends": ["../../biome.json"],
4 | "files": {
5 | "include": ["./src/", "./test/"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/token-tools/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@terrazzo/token-tools",
3 | "version": "0.8.0",
4 | "description": "Various utilities for token types",
5 | "license": "MIT",
6 | "type": "module",
7 | "author": {
8 | "name": "Drew Powers",
9 | "email": "drew@pow.rs"
10 | },
11 | "keywords": [
12 | "design tokens",
13 | "dtcg",
14 | "cli",
15 | "w3c",
16 | "design system",
17 | "typescript",
18 | "sass",
19 | "css",
20 | "style tokens",
21 | "style system",
22 | "linting",
23 | "lint"
24 | ],
25 | "repository": {
26 | "type": "git",
27 | "url": "https://github.com/terrazzoapp/terrazzo.git",
28 | "directory": "./packages/token-tools/"
29 | },
30 | "main": "./dist/index.js",
31 | "exports": {
32 | ".": "./dist/index.js",
33 | "./css": "./dist/css.js",
34 | "./js": "./dist/js.js",
35 | "./package.json": "./package.json"
36 | },
37 | "scripts": {
38 | "build": "rolldown -c && attw --profile esm-only --pack .",
39 | "dev": "rolldown -c -w",
40 | "lint": "pnpm --filter @terrazzo/token-tools run \"/^lint:(js|ts)/\"",
41 | "lint:js": "biome check .",
42 | "lint:ts": "tsc --noEmit",
43 | "format": "biome check --fix --unsafe .",
44 | "test": "vitest run"
45 | },
46 | "dependencies": {
47 | "@humanwhocodes/momoa": "^3.3.8",
48 | "culori": "^4.0.1",
49 | "wildcard-match": "^5.1.4"
50 | },
51 | "devDependencies": {
52 | "@types/culori": "^4.0.0"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/packages/token-tools/rolldown.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'rolldown';
2 | import { dts } from 'rolldown-plugin-dts';
3 |
4 | export default defineConfig({
5 | input: {
6 | index: './src/index.ts',
7 | css: './src/css/index.ts',
8 | js: './src/js/index.ts',
9 | },
10 | plugins: [dts()],
11 | external: ['@humanwhocodes/momoa', 'culori', 'culori/css', 'culori/fn', 'wildcard-match'],
12 | output: {
13 | dir: 'dist',
14 | format: 'es',
15 | sourcemap: true,
16 | },
17 | });
18 |
--------------------------------------------------------------------------------
/packages/token-tools/src/css/boolean.ts:
--------------------------------------------------------------------------------
1 | import type { BooleanTokenNormalized } from '../types.js';
2 | import type { TransformCSSValueOptions } from './css-types.js';
3 | import { defaultAliasTransform } from './lib.js';
4 |
5 | /** Convert boolean value to CSS string */
6 | export function transformBoolean(
7 | token: BooleanTokenNormalized,
8 | { tokensSet, transformAlias = defaultAliasTransform }: TransformCSSValueOptions,
9 | ): string {
10 | if (token.aliasChain?.[0]) {
11 | return transformAlias(tokensSet[token.aliasChain[0]]!);
12 | }
13 | return token.$value === true ? '1' : '0';
14 | }
15 |
--------------------------------------------------------------------------------
/packages/token-tools/src/css/css-types.ts:
--------------------------------------------------------------------------------
1 | import type { TokenNormalizedSet } from '../types.js';
2 | import type { IDGenerator } from './lib.js';
3 |
4 | export interface TransformCSSValueOptions {
5 | /** Complete set of tokens (needed to resolve full and partial aliases) */
6 | tokensSet: TokenNormalizedSet;
7 | transformAlias?: IDGenerator;
8 | /** Options for color tokens */
9 | color?: {
10 | /** Output legacy hex-6 and hex-8 */
11 | legacyHex?: boolean;
12 | };
13 | }
14 |
--------------------------------------------------------------------------------
/packages/token-tools/src/css/cubic-bezier.ts:
--------------------------------------------------------------------------------
1 | import type { CubicBezierTokenNormalized } from '../types.js';
2 | import type { TransformCSSValueOptions } from './css-types.js';
3 | import { defaultAliasTransform } from './lib.js';
4 |
5 | /** Convert cubicBezier value to CSS */
6 | export function transformCubicBezier(token: CubicBezierTokenNormalized, options: TransformCSSValueOptions): string {
7 | const { tokensSet, transformAlias = defaultAliasTransform } = options;
8 | if (token.aliasChain?.[0]) {
9 | return transformAlias(tokensSet[token.aliasChain[0]]!);
10 | }
11 | return `cubic-bezier(${token.$value
12 | .map((v, i) => (token.partialAliasOf?.[i] ? transformAlias(tokensSet[token.partialAliasOf[i]]!) : v))
13 | .join(', ')})`;
14 | }
15 |
--------------------------------------------------------------------------------
/packages/token-tools/src/css/dimension.ts:
--------------------------------------------------------------------------------
1 | import type { DimensionTokenNormalized } from '../types.js';
2 | import type { TransformCSSValueOptions } from './css-types.js';
3 | import { defaultAliasTransform } from './lib.js';
4 |
5 | /** Convert dimension value to CSS */
6 | export function transformDimension(token: DimensionTokenNormalized, options: TransformCSSValueOptions): string {
7 | const { tokensSet, transformAlias = defaultAliasTransform } = options;
8 | if (token.aliasChain?.[0]) {
9 | return transformAlias(tokensSet[token.aliasChain[0]]!);
10 | }
11 |
12 | return token.$value.value === 0 ? '0' : `${token.$value.value}${token.$value.unit}`;
13 | }
14 |
--------------------------------------------------------------------------------
/packages/token-tools/src/css/duration.ts:
--------------------------------------------------------------------------------
1 | import type { DurationTokenNormalized } from '../types.js';
2 | import type { TransformCSSValueOptions } from './css-types.js';
3 | import { defaultAliasTransform } from './lib.js';
4 |
5 | /** Convert duration value to CSS */
6 | export function transformDuration(token: DurationTokenNormalized, options: TransformCSSValueOptions): string {
7 | const { tokensSet, transformAlias = defaultAliasTransform } = options;
8 | if (token.aliasChain?.[0]) {
9 | return transformAlias(tokensSet[token.aliasChain[0]]!);
10 | }
11 |
12 | return `${token.$value.value}${token.$value.unit}`;
13 | }
14 |
--------------------------------------------------------------------------------
/packages/token-tools/src/css/font-family.ts:
--------------------------------------------------------------------------------
1 | import type { FontFamilyTokenNormalized } from '../types.js';
2 | import type { TransformCSSValueOptions } from './css-types.js';
3 | import { defaultAliasTransform } from './lib.js';
4 |
5 | const FONT_NAME_KEYWORD = /^[a-z-]+$/;
6 |
7 | export function transformFontFamily(token: FontFamilyTokenNormalized, options: TransformCSSValueOptions): string {
8 | const { tokensSet, transformAlias = defaultAliasTransform } = options;
9 | if (token.aliasChain?.[0]) {
10 | return transformAlias(tokensSet[token.aliasChain[0]]!);
11 | }
12 | return token.$value.map((fontName) => (FONT_NAME_KEYWORD.test(fontName) ? fontName : `"${fontName}"`)).join(', ');
13 | }
14 |
--------------------------------------------------------------------------------
/packages/token-tools/src/css/font-weight.ts:
--------------------------------------------------------------------------------
1 | import type { FontWeightTokenNormalized } from '../types.js';
2 | import type { TransformCSSValueOptions } from './css-types.js';
3 | import { defaultAliasTransform } from './lib.js';
4 |
5 | /** Convert fontWeight value to CSS */
6 | export function transformFontWeight(token: FontWeightTokenNormalized, options: TransformCSSValueOptions): string {
7 | const { tokensSet, transformAlias = defaultAliasTransform } = options;
8 | if (token.aliasChain?.[0]) {
9 | return transformAlias(tokensSet[token.aliasChain[0]]!);
10 | }
11 | return String(token.$value);
12 | }
13 |
--------------------------------------------------------------------------------
/packages/token-tools/src/css/link.ts:
--------------------------------------------------------------------------------
1 | import type { LinkTokenNormalized } from '../types.js';
2 | import type { TransformCSSValueOptions } from './css-types.js';
3 | import { defaultAliasTransform } from './lib.js';
4 |
5 | /** Convert link value to CSS */
6 | export function transformLink(token: LinkTokenNormalized, options: TransformCSSValueOptions): string {
7 | const { tokensSet, transformAlias = defaultAliasTransform } = options;
8 | if (token.aliasChain?.[0]) {
9 | return transformAlias(tokensSet[token.aliasChain[0]]!);
10 | }
11 | return `url("${token.$value}")`;
12 | }
13 |
--------------------------------------------------------------------------------
/packages/token-tools/src/css/number.ts:
--------------------------------------------------------------------------------
1 | import type { NumberTokenNormalized } from '../types.js';
2 | import type { TransformCSSValueOptions } from './css-types.js';
3 | import { defaultAliasTransform } from './lib.js';
4 |
5 | /** Convert number value to CSS */
6 | export function transformNumber(token: NumberTokenNormalized, options: TransformCSSValueOptions): string {
7 | const { tokensSet, transformAlias = defaultAliasTransform } = options;
8 | if (token.aliasChain?.[0]) {
9 | return transformAlias(tokensSet[token.aliasChain[0]]!);
10 | }
11 | return String(token.$value);
12 | }
13 |
--------------------------------------------------------------------------------
/packages/token-tools/src/css/string.ts:
--------------------------------------------------------------------------------
1 | import type { StringTokenNormalized } from '../types.js';
2 | import type { TransformCSSValueOptions } from './css-types.js';
3 | import { defaultAliasTransform } from './lib.js';
4 |
5 | /** Convert string value to CSS */
6 | export function transformString(token: StringTokenNormalized, options: TransformCSSValueOptions): string {
7 | const { tokensSet, transformAlias = defaultAliasTransform } = options;
8 | if (token.aliasChain?.[0]) {
9 | return transformAlias(tokensSet[token.aliasChain[0]]!);
10 | }
11 | // this seems like a useless function—because it is—but this is a placeholder
12 | // that can handle unexpected values in the future should any arise
13 | return String(token.$value);
14 | }
15 |
--------------------------------------------------------------------------------
/packages/token-tools/src/css/stroke-style.ts:
--------------------------------------------------------------------------------
1 | import type { StrokeStyleTokenNormalized } from '../types.js';
2 | import type { TransformCSSValueOptions } from './css-types.js';
3 | import { defaultAliasTransform } from './lib.js';
4 |
5 | /** Convert strokeStyle value to CSS */
6 | export function transformStrokeStyle(token: StrokeStyleTokenNormalized, options: TransformCSSValueOptions): string {
7 | const { tokensSet, transformAlias = defaultAliasTransform } = options;
8 | if (token.aliasChain?.[0]) {
9 | return transformAlias(tokensSet[token.aliasChain[0]]!);
10 | }
11 | return typeof token.$value === 'string' ? token.$value : 'dashed'; // CSS doesn’t have `dash-array`; it’s just "dashed"
12 | }
13 |
--------------------------------------------------------------------------------
/packages/token-tools/src/css/transition.ts:
--------------------------------------------------------------------------------
1 | import type { CubicBezierTokenNormalized, DurationTokenNormalized, TransitionTokenNormalized } from '../types.js';
2 | import type { TransformCSSValueOptions } from './css-types.js';
3 | import { transformCubicBezier } from './cubic-bezier.js';
4 | import { transformDuration } from './duration.js';
5 | import { defaultAliasTransform } from './lib.js';
6 |
7 | /** Convert transition value to shorthand */
8 | export function transformTransition(token: TransitionTokenNormalized, options: TransformCSSValueOptions) {
9 | const { tokensSet, transformAlias = defaultAliasTransform } = options;
10 | if (token.aliasChain?.[0]) {
11 | return transformAlias(tokensSet[token.aliasChain[0]]!);
12 | }
13 |
14 | const duration = token.partialAliasOf?.duration
15 | ? transformAlias(tokensSet[token.partialAliasOf.duration]!)
16 | : transformDuration({ $value: token.$value.duration } as DurationTokenNormalized, options);
17 | const delay = token.partialAliasOf?.delay
18 | ? transformAlias(tokensSet[token.partialAliasOf.delay]!)
19 | : transformDuration({ $value: token.$value.delay } as DurationTokenNormalized, options);
20 | const timingFunction = token.partialAliasOf?.timingFunction
21 | ? transformAlias(tokensSet[token.partialAliasOf.timingFunction]!)
22 | : transformCubicBezier({ $value: token.$value.timingFunction } as CubicBezierTokenNormalized, options);
23 |
24 | return `${duration} ${delay} ${timingFunction}`;
25 | }
26 |
--------------------------------------------------------------------------------
/packages/token-tools/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './color.js';
2 | export * from './id.js';
3 | export * from './string.js';
4 | export * from './transform.js';
5 | export * from './types.js';
6 |
--------------------------------------------------------------------------------
/packages/token-tools/src/js/index.ts:
--------------------------------------------------------------------------------
1 | import type { TokenNormalized } from '../types.js';
2 |
3 | export interface TransformJSValueOptions {
4 | mode: string;
5 | /** indent space count */
6 | indent?: number;
7 | /** initial indent level */
8 | startingIndent?: number;
9 | }
10 |
11 | /**
12 | * Convert token value to a JS string via acorn/astring.
13 | */
14 | export function transformJSValue(
15 | token: T,
16 | { mode, indent = 2, startingIndent = 0 }: TransformJSValueOptions,
17 | ) {
18 | if (!(mode in token.mode)) {
19 | return;
20 | }
21 | const indentStart = startingIndent > 0 ? ' '.repeat(startingIndent ?? 2) : '';
22 |
23 | // note: since tokens are JSON-serializable to begin with, using
24 | // JSON.stringify generates the same output as an AST parser/generator would
25 | // but faster and without the added overhead (even acorn/astring leave object
26 | // keys quoted).
27 |
28 | // TODO: use @biomejs/js-api when it’s stable for pretty formatting
29 |
30 | return JSON.stringify(token.mode[mode]!.$value, undefined, indent).replace(/\n/g, `\n${indentStart}`);
31 | }
32 |
--------------------------------------------------------------------------------
/packages/token-tools/src/transform.ts:
--------------------------------------------------------------------------------
1 | export interface CustomTransformOptions {
2 | /** Token $type */
3 | $type: string;
4 | }
5 |
6 | /** Give a user pertinent feedback if they override a transform incorrectly */
7 | export function validateCustomTransform(value: unknown, { $type }: CustomTransformOptions) {
8 | if (value) {
9 | if ((typeof value !== 'string' && typeof value !== 'object') || Array.isArray(value)) {
10 | throw new Error(
11 | `transform(): expected string or Object of strings, received ${Array.isArray(value) ? 'Array' : typeof value}`,
12 | );
13 | }
14 | switch ($type) {
15 | case 'typography': {
16 | if (typeof value !== 'object') {
17 | throw new Error('transform(): typography tokens must be an object of keys');
18 | }
19 | break;
20 | }
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/packages/token-tools/test/types.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, expectTypeOf, it } from 'vitest';
2 | import type { Group } from '../src/types.js';
3 |
4 | describe('types', () => {
5 | it('group', () => {
6 | const simpleGroup = {
7 | $description: 'group',
8 | color: {
9 | $type: 'color',
10 | $description: 'token',
11 | $value: '#000000',
12 | },
13 | } satisfies Group;
14 |
15 | expectTypeOf(simpleGroup).toMatchTypeOf();
16 | });
17 |
18 | it('nested groups', () => {
19 | const groupsWithCore = {
20 | $description: 'group',
21 | color: {
22 | $description: 'nested group',
23 | primary: {
24 | $type: 'color',
25 | $description: 'token',
26 | $value: '#000000',
27 | },
28 | },
29 | } satisfies Group;
30 |
31 | expectTypeOf(groupsWithCore).toMatchTypeOf();
32 |
33 | const groupsWithoutCore = {
34 | color: {
35 | primary: {
36 | $type: 'color',
37 | $value: '#000000',
38 | },
39 | },
40 | } satisfies Group;
41 |
42 | expectTypeOf(groupsWithoutCore).toMatchTypeOf();
43 | });
44 | });
45 |
--------------------------------------------------------------------------------
/packages/token-tools/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "exclude": ["test"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/token-tools/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "outDir": "dist"
5 | },
6 | "include": ["./src/", "./test/"]
7 | }
8 |
--------------------------------------------------------------------------------
/packages/token-tools/vitest.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vitest/config';
2 |
3 | export default defineConfig({
4 | test: {
5 | restoreMocks: true,
6 | },
7 | });
8 |
--------------------------------------------------------------------------------
/packages/token-tools/vitest.config.ts.timestamp-1718599329856-9fd330128a63e.mjs:
--------------------------------------------------------------------------------
1 | // vitest.config.ts
2 | import { defineConfig } from "file:///Users/drew/Sites/terrazzoapp/terrazzo/node_modules/.pnpm/vitest@1.6.0_@types+node@20.14.2/node_modules/vitest/dist/config.js";
3 | var vitest_config_default = defineConfig({
4 | test: {
5 | restoreMocks: true
6 | }
7 | });
8 | export {
9 | vitest_config_default as default
10 | };
11 | //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZXN0LmNvbmZpZy50cyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsiY29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2Rpcm5hbWUgPSBcIi9Vc2Vycy9kcmV3L1NpdGVzL3RlcnJhenpvYXBwL3RlcnJhenpvL3BhY2thZ2VzL3Rva2VuLXRvb2xzXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ZpbGVuYW1lID0gXCIvVXNlcnMvZHJldy9TaXRlcy90ZXJyYXp6b2FwcC90ZXJyYXp6by9wYWNrYWdlcy90b2tlbi10b29scy92aXRlc3QuY29uZmlnLnRzXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ltcG9ydF9tZXRhX3VybCA9IFwiZmlsZTovLy9Vc2Vycy9kcmV3L1NpdGVzL3RlcnJhenpvYXBwL3RlcnJhenpvL3BhY2thZ2VzL3Rva2VuLXRvb2xzL3ZpdGVzdC5jb25maWcudHNcIjtpbXBvcnQgeyBkZWZpbmVDb25maWcgfSBmcm9tICd2aXRlc3QvY29uZmlnJztcblxuZXhwb3J0IGRlZmF1bHQgZGVmaW5lQ29uZmlnKHtcbiAgdGVzdDoge1xuICAgIHJlc3RvcmVNb2NrczogdHJ1ZSxcbiAgfSxcbn0pO1xuIl0sCiAgIm1hcHBpbmdzIjogIjtBQUF1VyxTQUFTLG9CQUFvQjtBQUVwWSxJQUFPLHdCQUFRLGFBQWE7QUFBQSxFQUMxQixNQUFNO0FBQUEsSUFDSixjQUFjO0FBQUEsRUFDaEI7QUFDRixDQUFDOyIsCiAgIm5hbWVzIjogW10KfQo=
12 |
--------------------------------------------------------------------------------
/packages/tokens/.gitignore:
--------------------------------------------------------------------------------
1 | !dist
2 |
--------------------------------------------------------------------------------
/packages/tokens/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
3 | "extends": ["../../biome.json"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/tokens/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@terrazzo/tokens",
3 | "version": "0.1.0",
4 | "description": "Internal design tokens for @terrazzo/tiles",
5 | "type": "module",
6 | "author": {
7 | "name": "Drew Powers",
8 | "email": "drew@pow.rs"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "https://github.com/terrazzoapp/terrazzo.git",
13 | "directory": "./packages/tokens/"
14 | },
15 | "scripts": {
16 | "build": "tz build",
17 | "lint": "pnpm run lint:tokens",
18 | "lint:tokens": "tz check",
19 | "dev": "tz build --watch"
20 | },
21 | "devDependencies": {
22 | "@terrazzo/cli": "workspace:^",
23 | "@terrazzo/plugin-css": "workspace:^",
24 | "@terrazzo/token-tools": "workspace:^"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/packages/tokens/terrazzo.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from '@terrazzo/cli';
2 | import { makeCSSVar } from '@terrazzo/token-tools/css';
3 | import css from '@terrazzo/plugin-css';
4 |
5 | export default defineConfig({
6 | tokens: './tokens.json',
7 | outDir: './dist/',
8 | plugins: [
9 | css({
10 | variableName: (token) => makeCSSVar(token.id, { prefix: 'tz' }),
11 | modeSelectors: [
12 | {
13 | mode: 'light',
14 | selectors: ['@media (prefers-color-scheme: light)', '[data-color-mode="light"][data-color-mode="light"]'],
15 | },
16 | {
17 | mode: 'dark',
18 | selectors: ['@media (prefers-color-scheme: dark)', '[data-color-mode="dark"][data-color-mode="dark"]'],
19 | },
20 | ],
21 | }),
22 | ],
23 | });
24 |
--------------------------------------------------------------------------------
/packages/use-color/.npmignore:
--------------------------------------------------------------------------------
1 | *.test.*
2 | .size-limit.js
3 | .turbo
4 | biome.*
5 | vite.*
6 | vitest.*
7 | tsconfig.*
8 |
--------------------------------------------------------------------------------
/packages/use-color/.size-limit.js:
--------------------------------------------------------------------------------
1 | export default [{ ignore: ['culori'], path: './index.js', limit: '12 kB' }];
2 |
--------------------------------------------------------------------------------
/packages/use-color/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @terrazzo/use-color
2 |
3 | ## 0.0.7
4 |
5 | ### Patch Changes
6 |
7 | - Bugfixes thanks to [@lilnasy](lilnasy)
8 |
--------------------------------------------------------------------------------
/packages/use-color/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
3 | "extends": ["../../biome.json"],
4 | "linter": {
5 | "rules": {
6 | "correctness": {
7 | "useHookAtTopLevel": "off"
8 | }
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/use-color/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@terrazzo/use-color",
3 | "version": "0.1.0",
4 | "description": "React hook for memoizing and transforming any web-compatible color. Uses Culori.",
5 | "license": "MIT",
6 | "type": "module",
7 | "keywords": ["color", "color-module-4", "culori", "react", "hooks"],
8 | "author": {
9 | "name": "Drew Powers",
10 | "email": "drew@pow.rs"
11 | },
12 | "main": "./index.js",
13 | "homepage": "https://terrazzo.app/docs/components/color-picker",
14 | "repository": {
15 | "type": "git",
16 | "url": "https://github.com/terrazzoapp/terrazzo.git",
17 | "directory": "./packages/use-color/"
18 | },
19 | "scripts": {
20 | "build": "tsc --noEmit && size-limit",
21 | "format": "biome check --fix --unsafe .",
22 | "lint": "biome check .",
23 | "test": "vitest run"
24 | },
25 | "peerDependencies": {
26 | "react": "^19.0.0"
27 | },
28 | "dependencies": {
29 | "@terrazzo/token-tools": "workspace:^",
30 | "@types/culori": "^4.0.0",
31 | "culori": "^4.0.1"
32 | },
33 | "devDependencies": {
34 | "@size-limit/preset-small-lib": "^11.2.0",
35 | "@testing-library/jest-dom": "^6.6.3",
36 | "@testing-library/react": "^16.3.0",
37 | "@testing-library/user-event": "^14.6.1",
38 | "@types/react": "^19.1.1",
39 | "@types/react-dom": "^19.1.2",
40 | "@vitejs/plugin-react-swc": "^3.8.1",
41 | "react": "19.0.0",
42 | "react-dom": "19.0.0",
43 | "size-limit": "^11.2.0"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/packages/use-color/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.react.json",
3 | "compilerOptions": {
4 | "baseUrl": "src",
5 | "noEmit": true,
6 | "outDir": "dist"
7 | },
8 | "include": ["./*.{js,ts}", "./index.test.tsx"]
9 | }
10 |
--------------------------------------------------------------------------------
/packages/use-color/vitest.config.ts:
--------------------------------------------------------------------------------
1 | import react from '@vitejs/plugin-react-swc';
2 | import { defineConfig } from 'vitest/config';
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | test: {
8 | environment: 'jsdom',
9 | restoreMocks: true,
10 | setupFiles: ['./vitest.setup.ts'],
11 | testTimeout: 15000,
12 | },
13 | });
14 |
--------------------------------------------------------------------------------
/packages/use-color/vitest.setup.ts:
--------------------------------------------------------------------------------
1 | import '@testing-library/jest-dom/vitest';
2 | import { cleanup } from '@testing-library/react';
3 | import { afterEach } from 'vitest';
4 |
5 | // @see https://github.com/vitest-dev/vitest/issues/1430
6 | afterEach(cleanup);
7 |
--------------------------------------------------------------------------------
/patches/vite-plugin-sass-dts.patch:
--------------------------------------------------------------------------------
1 | diff --git a/dist/index.js b/dist/index.js
2 | index b4fbd644ea9430b83addd30a1adb9c092fdb3941..ad09b4ad5e8d2d72161a9a3242623134b4900071 100644
3 | --- a/dist/index.js
4 | +++ b/dist/index.js
5 | @@ -360,7 +360,7 @@ var formatWriteFilePath = (file, options) => {
6 | }
7 | return formatWriteFileName(path4);
8 | };
9 | -var formatWriteFileName = (file) => `${file}${file.endsWith("d.ts") ? "" : ".d.ts"}`;
10 | +var formatWriteFileName = (file) => file.replace(/\.css$/, ".d.css.ts");
11 | var formatExportTypeFileName = (file) => basename(file.replace(".ts", ""));
12 | var ensureDirectoryExists = async (file) => {
13 | await mkdir(dirname(file), { recursive: true });
14 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | shellEmulator: true
2 | packages:
3 | - packages/**
4 | - www
5 | - '!packages/**/test/**'
6 | onlyBuiltDependencies:
7 | - '@biomejs/biome'
8 | - '@swc/core'
9 | - esbuild
10 | - sharp
11 |
--------------------------------------------------------------------------------
/scripts/inject-license.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | import fs from 'node:fs';
4 |
5 | // settings
6 | const LB_RE = /\n/g;
7 | const license = fs
8 | .readFileSync(new URL('../LICENSE', import.meta.url), 'utf8')
9 | .trim()
10 | .replace(LB_RE, '\n * ');
11 |
12 | // run
13 | const cwd = new URL(`file://${process.env.INIT_CWD ?? process.cwd()}/`);
14 | const [, , module, targets] = process.argv;
15 |
16 | for (const target of targets.split(',')) {
17 | const filepath = new URL(target, cwd);
18 | const contents = fs.readFileSync(filepath, 'utf8');
19 |
20 | fs.writeFileSync(
21 | filepath,
22 | `/**
23 | * @module ${module}
24 | * @license ${license}
25 | */
26 | ${contents}`,
27 | );
28 | }
29 |
--------------------------------------------------------------------------------
/stylelint.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('stylelint').Config} */
2 | export default {
3 | extends: ['stylelint-config-standard'],
4 | plugins: ['stylelint-order'],
5 | ignoreFiles: ['**/*.js', '**/*.ts', '**/*.tsx', '**/test/*'],
6 | rules: {
7 | 'alpha-value-notation': 'number',
8 | 'declaration-block-no-redundant-longhand-properties': null, // this is fine
9 | 'hue-degree-notation': 'number',
10 | 'lightness-notation': 'number',
11 | 'no-descending-specificity': null, // this often leads to unsolvable riddles
12 | 'number-max-precision': null, // in the running for dumbest rule ever created
13 | 'order/properties-alphabetical-order': true, // let me find things
14 | 'property-no-vendor-prefix': null, // we still need this
15 | 'selector-class-pattern': null,
16 | 'selector-pseudo-class-no-unknown': null, // stylelint considers the :global selector unknown
17 | },
18 | };
19 |
--------------------------------------------------------------------------------
/tsconfig.base.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowArbitraryExtensions": true,
4 | "declaration": true,
5 | "declarationMap": true,
6 | "esModuleInterop": true,
7 | "lib": ["ESNext"],
8 | "module": "NodeNext",
9 | "moduleResolution": "nodenext",
10 | "noEmitOnError": true,
11 | "noFallthroughCasesInSwitch": true,
12 | "noUncheckedIndexedAccess": true,
13 | "noUnusedLocals": true,
14 | "noUnusedParameters": true,
15 | "resolveJsonModule": true,
16 | "skipLibCheck": true,
17 | "sourceMap": true,
18 | "strict": true,
19 | "target": "ESNext",
20 | "verbatimModuleSyntax": true
21 | },
22 | "exclude": ["**/dist/*", "**/node_modules/*"]
23 | }
24 |
--------------------------------------------------------------------------------
/tsconfig.react.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.base.json",
3 | "compilerOptions": {
4 | "lib": ["DOM", "ESNext"],
5 | "jsx": "react-jsx",
6 | "jsxImportSource": "react"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/turbo.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://turbo.build/schema.json",
3 | "tasks": {
4 | "build": {
5 | "dependsOn": ["^build"]
6 | },
7 | "build:app": {
8 | "dependsOn": ["build"]
9 | },
10 | "format": {
11 | "dependsOn": ["^format"]
12 | },
13 | "lint": {
14 | "inputs": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.css", "**/biome.json"],
15 | "dependsOn": ["build"]
16 | },
17 | "test": {
18 | "dependsOn": ["build"]
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/www/.gitignore:
--------------------------------------------------------------------------------
1 | /.astro
2 | /.cache
3 | /build
4 | .env
5 |
--------------------------------------------------------------------------------
/www/README.md:
--------------------------------------------------------------------------------
1 | # ⛋ www + docs
2 |
3 | ```sh
4 | pnpm i
5 | pnpm run dev
6 | ```
7 |
--------------------------------------------------------------------------------
/www/astro.config.ts:
--------------------------------------------------------------------------------
1 | import react from '@astrojs/react';
2 | import sitemap from '@astrojs/sitemap';
3 | import mdx from '@astrojs/mdx';
4 | import { defineConfig } from 'astro/config';
5 | import rehypeAutolinkHeadings from 'rehype-autolink-headings';
6 | import rehypeSlug from 'rehype-slug';
7 | import remarkDirective from 'remark-directive';
8 | import rehypeAutoToc from './src/plugins/rehype-auto-toc.js';
9 | import remarkVitepress from './src/plugins/remark-vitepress.js';
10 |
11 | // https://astro.build/config
12 | export default defineConfig({
13 | integrations: [react(), mdx(), sitemap()],
14 | site: 'https://terrazzo.app',
15 | devToolbar: {
16 | enabled: false,
17 | },
18 | markdown: {
19 | shikiConfig: {
20 | theme: 'ayu-dark',
21 | },
22 | remarkRehype: {
23 | allowDangerousHtml: true,
24 | },
25 | remarkPlugins: [remarkDirective, remarkVitepress],
26 | rehypePlugins: [rehypeSlug, rehypeAutolinkHeadings, rehypeAutoToc],
27 | },
28 | });
29 |
--------------------------------------------------------------------------------
/www/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
3 | "extends": ["../biome.json"]
4 | }
5 |
--------------------------------------------------------------------------------
/www/public/assets/apple-hig-typography.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terrazzoapp/terrazzo/ac939ed496a483874e2fc6ffb949cc70f647593f/www/public/assets/apple-hig-typography.png
--------------------------------------------------------------------------------
/www/public/assets/cli-init.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terrazzoapp/terrazzo/ac939ed496a483874e2fc6ffb949cc70f647593f/www/public/assets/cli-init.png
--------------------------------------------------------------------------------
/www/public/assets/colorpicker-gamut.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terrazzoapp/terrazzo/ac939ed496a483874e2fc6ffb949cc70f647593f/www/public/assets/colorpicker-gamut.png
--------------------------------------------------------------------------------
/www/public/assets/github-themes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terrazzoapp/terrazzo/ac939ed496a483874e2fc6ffb949cc70f647593f/www/public/assets/github-themes.png
--------------------------------------------------------------------------------
/www/public/assets/radix-colors.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terrazzoapp/terrazzo/ac939ed496a483874e2fc6ffb949cc70f647593f/www/public/assets/radix-colors.png
--------------------------------------------------------------------------------
/www/public/assets/social-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terrazzoapp/terrazzo/ac939ed496a483874e2fc6ffb949cc70f647593f/www/public/assets/social-image.png
--------------------------------------------------------------------------------
/www/public/assets/tooltip-dialog-light-dark-mode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terrazzoapp/terrazzo/ac939ed496a483874e2fc6ffb949cc70f647593f/www/public/assets/tooltip-dialog-light-dark-mode.png
--------------------------------------------------------------------------------
/www/public/favicon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/www/public/robots.txt:
--------------------------------------------------------------------------------
1 | # Example: Allow all bots to scan and index your site.
2 | # Full syntax: https://developers.google.com/search/docs/advanced/robots/create-robots-txt
3 | User-agent: *
4 | Allow: /
5 |
--------------------------------------------------------------------------------
/www/src/components/tri.astro:
--------------------------------------------------------------------------------
1 | ---
2 | export interface Props {
3 | color?: string;
4 | style?: string;
5 | }
6 |
7 | const { color = 'var(--tz-color-base-gray-100)', style } = Astro.props;
8 | ---
9 |
10 |
11 |
12 |
27 |
--------------------------------------------------------------------------------
/www/src/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
--------------------------------------------------------------------------------
/www/src/layouts/default.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import HeadMeta from '../components/head-meta.astro';
3 | import MainFooter from '../components/main-footer.astro';
4 | import MainNav from '../components/main-nav.astro';
5 |
6 | const { title, description, socialImage } = Astro.props;
7 | ---
8 |
9 |
10 |
11 |
16 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/www/src/lib/navigation.ts:
--------------------------------------------------------------------------------
1 | export function getAriaCurrent(testPathname: string, currentPathname: string | URL): 'page' | 'location' | undefined {
2 | const currentLocation = new URL(currentPathname);
3 | if (currentLocation.pathname === testPathname) {
4 | return 'page';
5 | }
6 | if (testPathname !== '/' && currentLocation.pathname.startsWith(testPathname)) {
7 | return 'location';
8 | }
9 | return undefined;
10 | }
11 |
--------------------------------------------------------------------------------
/www/src/pages/docs/cli/integrations/custom.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Custom Integration
3 | layout: ../../../../layouts/docs.astro
4 | ---
5 |
6 | # Custom Integration
7 |
8 | Building a custom integration is easier than you might think! See the [Plugin API docs](/docs/cli/api/plugin-development) for instructions on how to build your own Terrazzo plugin from scratch.
9 |
--------------------------------------------------------------------------------
/www/src/pages/docs/cli/integrations/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Integrations
3 | layout: ../../../../layouts/docs.astro
4 | ---
5 |
6 | # Integrations
7 |
8 | - [**CSS**](/docs/cli/integrations/css)
9 | - [**JS/TS**](/docs/cli/integrations/js)
10 | - [**JSON/Native**](/docs/cli/integrations/json)
11 | - [**Sass**](/docs/cli/integrations/sass)
12 | - [**Tailwind**](/docs/cli/integrations/tailwind)
13 |
--------------------------------------------------------------------------------
/www/src/pages/docs/cli/integrations/swift.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: CSS
3 | layout: ../../../../layouts/docs.astro
4 | ---
5 |
6 | # Swift
7 |
8 | Generate Swift asset catalogs from DTCG tokens.
9 |
10 | ## Setup
11 |
12 | Requires [Node.js 20 or later](https://nodejs.org). With that installed, and a `package.json`, run:
13 |
14 | ```sh
15 | npm i -D @terrazzo/cli @terrazzo/plugin-swift
16 | ```
17 |
18 | Add a `terrazzo.config.js` to the root of your project with:
19 |
20 | ```ts
21 | import { defineConfig } from "@terrazzo/cli";
22 | import swift from "@terrazzo/plugin-swift";
23 |
24 | export default defineConfig({
25 | outDir: "./tokens/",
26 | plugins: [
27 | swift({
28 | catalogName: "Tokens",
29 | }),
30 | ],
31 | });
32 | ```
33 |
34 | Lastly, run:
35 |
36 | ```sh
37 | npx tz build
38 | ```
39 |
40 | And you’ll see a `./tokens/Tokens.xcassets` catalog generated in your project. [Import it](https://developer.apple.com/documentation/xcode/managing-assets-with-asset-catalogs) into your project and you’ll have access to all your tokens!
41 |
--------------------------------------------------------------------------------
/www/src/pages/docs/components/demo.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Demo
3 | layout: ../../../layouts/docs.astro
4 | ---
5 |
6 | import DemoButton from "./demos/demo-button.tsx";
7 |
8 | # Demo
9 |
10 | A component for showing a component sandbox and displaying a copyable code snippet next to it.
11 |
12 | ## Setup
13 |
14 | :::code-group
15 |
16 | ```sh npm
17 | npm i @terrazzo/tiles
18 | ```
19 |
20 | ```sh pnpm
21 | pnpm i @terrazzo/tiles
22 | ```
23 |
24 | :::
25 |
26 | ## Demo
27 |
28 |
29 |
--------------------------------------------------------------------------------
/www/src/pages/docs/components/demos/color-picker-basic.tsx:
--------------------------------------------------------------------------------
1 | import ColorPicker from '@terrazzo/react-color-picker';
2 | import { Demo } from '@terrazzo/tiles';
3 | import useColor from '@terrazzo/use-color';
4 |
5 | export default function ColorPickerDemo() {
6 | const [color, setColor] = useColor('#663399');
7 |
8 | return (
9 |
20 | );
21 | }`
22 | }
23 | >
24 |
25 |
26 |
27 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/www/src/pages/docs/components/demos/demo-button.tsx:
--------------------------------------------------------------------------------
1 | import { Button, Demo } from '@terrazzo/tiles';
2 |
3 | export default function DemoButtonDemo() {
4 | return (
5 | Save\`}>
15 | Save
16 |
17 | );
18 | }`
19 | }
20 | >
21 | Save
22 |
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/www/src/pages/docs/components/demos/slider.tsx:
--------------------------------------------------------------------------------
1 | import { Demo, Slider } from '@terrazzo/tiles';
2 | import { useState } from 'react';
3 |
4 | export default function ColorPickerDemo() {
5 | const [value, setValue] = useState(50);
6 |
7 | return (
8 |
17 | );
18 | }`
19 | }
20 | >
21 |
22 |
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/www/src/pages/docs/components/tiles.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Tiles
3 | layout: ../../../layouts/docs.astro
4 | ---
5 |
6 | # Tiles
7 |
8 | Tiles is Terrazzo’s open source React component library used to build everything you see. Its main goal is to display and showcase an existing design system, so it’s a great fit for any kind of documentation, design-related or not.
9 |
10 | ## Setup
11 |
12 | :::code-group
13 |
14 | ```sh npm
15 | npm i @terrazzo/react-color-picker
16 | ```
17 |
18 | ```sh pnpm
19 | pnpm i @terrazzo/react-color-picker
20 | ```
21 |
22 | :::
23 |
24 | ## Storybook
25 |
26 | [terrazzo-storybook.pages.dev](https://terrazzo-storybook.pages.dev)
27 |
--------------------------------------------------------------------------------
/www/src/pages/docs/guides/dtcg.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: DTCG Tokens
3 | layout: ../../../layouts/docs.astro
4 | ---
5 |
6 | # DTCG Tokens
7 |
8 | The DTCG format is a [W3C Community Group specification](https://www.designtokens.org/) started in 2020 and aims to outline a standard, universal design token format that works for all forms of digital design (including web, print, native apps, and beyond).
9 |
10 | DTCG tokens are stored in JSON, and look something like the following:
11 |
12 | ```json
13 | {
14 | "rebeccapurple": {
15 | "$type": "color",
16 | "$value": {
17 | "colorSpace": "srgb",
18 | "components": [0.4, 0.2, 0.6]
19 | }
20 | }
21 | }
22 | ```
23 |
24 | By storing tokens in a universal format like JSON, you can centrally manage them and generate code for any output target including web and native apps.
25 |
26 | ## Format
27 |
28 | You can [find the official format here](https://tr.designtokens.org/format/). But for convenience, we also have [a list of token types](/docs/reference/tokens) Terrazzo supports.
29 |
--------------------------------------------------------------------------------
/www/src/pages/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Terrazzo (Beta)
3 | layout: ../../layouts/docs.astro
4 | ---
5 |
6 | # Terrazzo Token Tools (Beta)
7 |
8 | Terrazzo is a work-in-progress, but comprises:
9 |
10 | - [**Terrazzo CLI**](/docs/cli) (formerly known as “Cobalt UI”): use [DTCG ](https://designtokens.org) tokens
11 | - [**Terrazzo Tiles DS**](/docs/components): Design system for _documenting_ design systems
12 | - **Token Lab** _(Coming Soon)_: generate, manage, and save token sets
13 |
--------------------------------------------------------------------------------
/www/src/pages/docs/reference/about.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: About Terrazzo
3 | layout: ../../../layouts/docs.astro
4 | ---
5 |
6 | # About Terrazzo
7 |
8 | Terrazzo is a free, MIT-licensed open-source design tool built by @drwpow , @ntassone , and [many other contributors](https://github.com/drwpow/cobalt-ui).
9 |
10 | It started out as “Cobalt” in 2021 as tooling for the [W3C Design Token Community Group (DTCG)](https://designtokens.org) format. It was clear early on that a visual token editor was needed, but Drew found the CLI to be as big of a project as he could handle on his own. So along came Nick to design and envision Token Lab in 2024.
11 |
12 | The project was renamed to “Terrazzo” to better reflect how lots of colorful little shards of tokens fit together to create a beautiful mosaic when all put together. Also because [lots of things are named “Cobalt”](https://github.com/search?q=cobalt&type=repositories) (or similar-sounding names like “[Cobol](https://en.wikipedia.org/wiki/COBOL)”) and it became confusing.
13 |
--------------------------------------------------------------------------------
/www/src/pages/docs/token-lab/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Token Lab
3 | layout: ../../../layouts/docs.astro
4 | ---
5 |
6 | # Token Lab
7 |
8 | The token lab is a thing
9 |
--------------------------------------------------------------------------------
/www/src/pages/docs/tokens/modes.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Thinking in Modes
3 | layout: ../../../layouts/docs.astro
4 | ---
5 |
6 | # Thinking in Modes
7 |
--------------------------------------------------------------------------------
/www/src/pages/token-lab/index.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import DefaultLayout from '../../layouts/default.astro';
3 | ---
4 |
5 |
6 | Token Lab
7 |
8 |
--------------------------------------------------------------------------------
/www/src/scripts/copy-code.js:
--------------------------------------------------------------------------------
1 | const MESSAGE_DELAY = 1500;
2 |
3 | for (const btn of document.querySelectorAll('button[data-code]')) {
4 | let t;
5 |
6 | btn.addEventListener('click', async () => {
7 | await navigator.clipboard.writeText(btn.getAttribute('data-code'));
8 | const contentBefore = btn.innerHTML;
9 | const labelBefore = btn.getAttribute('aria-label');
10 |
11 | btn.setAttribute('aria-label', 'Copied!');
12 | btn.setAttribute('aria-live', 'polite');
13 | btn.innerHTML =
14 | ' ';
15 |
16 | t = setTimeout(() => {
17 | clearTimeout(t);
18 | btn.setAttribute('aria-label', labelBefore);
19 | btn.removeAttribute('aria-live');
20 | btn.innerHTML = contentBefore;
21 | }, MESSAGE_DELAY);
22 | });
23 | }
24 |
--------------------------------------------------------------------------------
/www/stylelint.config.js:
--------------------------------------------------------------------------------
1 | import base from '../stylelint.config.js';
2 |
3 | /** @type {import('stylelint').Config} */
4 | export default {
5 | ...base,
6 | };
7 |
--------------------------------------------------------------------------------
/www/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "astro/tsconfigs/base",
3 | "compilerOptions": {
4 | "allowJs": true,
5 | "baseUrl": "/Users/drew/Sites/terrazzoapp/terrazzo/www",
6 | "esModuleInterop": true,
7 | "forceConsistentCasingInFileNames": true,
8 | "isolatedModules": true,
9 | "jsx": "react-jsx",
10 | "jsxImportSource": "react",
11 | "lib": ["DOM", "DOM.Iterable", "ES2022"],
12 | "module": "NodeNext",
13 | "moduleResolution": "nodenext",
14 | "noEmit": true,
15 | "noEmitOnError": false,
16 | "resolveJsonModule": true,
17 | "types": ["vite/client"]
18 | },
19 | "include": ["./src./"]
20 | }
21 |
--------------------------------------------------------------------------------