diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts index 93c45f8e..01976df2 100644 --- a/docs/.vitepress/theme/index.ts +++ b/docs/.vitepress/theme/index.ts @@ -1,5 +1,4 @@ import defaultTheme from "vitepress/theme"; -import "../../../packages/theme/src/index.css"; import "./custom.css"; export default { diff --git a/packages/theme/package.json b/packages/theme/package.json index 52fb551f..551eb442 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -1,18 +1,37 @@ { "name": "@tapsioss/theme", - "version": "0.3.1", + "version": "0.4.0", "type": "module", "files": [ - "./dist/**/*.css" + "./dist" ], - "main": "./dist/index.css", + "types": "./dist/types.d.ts", "exports": { - ".": { - "default": "./dist/index.css" + "./css-variables": { + "default": "./dist/default-theme/tokens.css" + }, + "./*/css-variables": { + "default": "./dist/*/tokens.css" + }, + "./tokens": { + "default": "./dist/default-theme/tokens.js", + "types": "./dist/default-theme/tokens.d.ts" + }, + "./*/tokens": { + "default": "./dist/*/tokens.js", + "types": "./dist/*/tokens.d.ts" + }, + "./types": { + "default": "./dist/types.d.ts" } }, "scripts": { - "build": "tsx ./scripts/build.ts", + "clear": "shx rm -rf dist", + "prebuild": "run-s clear generate", + "build:transpile": "tsc --project tsconfig.build.json", + "build:copy-css": "tsx ./scripts/copy-css-vars.ts", + "build": "run-s build:*", + "generate": "tsx ./scripts/generate.ts", "release": "pnpm publish . --tag latest --access public" } } diff --git a/packages/theme/scripts/build.ts b/packages/theme/scripts/build.ts deleted file mode 100644 index da8f8d83..00000000 --- a/packages/theme/scripts/build.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* eslint-disable no-console */ -import { exec } from "node:child_process"; -import * as path from "node:path"; -import { promisify } from "node:util"; -import { ensureDirExists, getFileMeta } from "../../../scripts/utils.ts"; - -const execCmd = promisify(exec); - -const { dirname } = getFileMeta(import.meta.url); - -const packageDir = path.resolve(dirname, ".."); -const distPath = path.join(packageDir, "dist"); -const entryPoint = path.join(packageDir, "src/index.css"); -const outputPath = path.join(distPath, "index.css"); - -const compile = async () => { - console.log("👾 compiling..."); - - await execCmd(["shx", "rm", "-rf", distPath].join(" ")); - await ensureDirExists(distPath); - - const { stderr, stdout } = await execCmd( - [ - "postcss", - entryPoint, - "-o", - outputPath, - "--use", - "postcss-import", - "--use", - "postcss-combine-duplicated-selectors", - "--no-map", - ].join(" "), - ); - - if (stdout) console.log(stdout); - if (stderr) console.error(stderr); - - console.log("✅ compilation completed."); -}; - -void (async () => { - console.time("build"); - await compile(); - console.timeEnd("build"); -})(); -/* eslint-enable no-console */ diff --git a/packages/theme/scripts/copy-css-vars.ts b/packages/theme/scripts/copy-css-vars.ts new file mode 100644 index 00000000..66d43181 --- /dev/null +++ b/packages/theme/scripts/copy-css-vars.ts @@ -0,0 +1,36 @@ +/* eslint-disable no-console */ +import globAsync from "fast-glob"; +import * as fs from "node:fs/promises"; +import * as path from "node:path"; +import { getFileMeta } from "../../../scripts/utils.ts"; + +const { dirname } = getFileMeta(import.meta.url); + +const packageDir = path.resolve(dirname, ".."); +const srcDir = path.join(packageDir, "src"); +const distDir = path.join(packageDir, "dist"); +const varsGlobPath = path.join(srcDir, "**/tokens.css"); + +const copyCssVars = async () => { + const varsFiles = await globAsync(varsGlobPath); + const promises: Promise[] = []; + + for (const varsFile of varsFiles) { + const varsPath = path.normalize(varsFile); + const themeDir = path.dirname(varsPath); + const theme = path.basename(themeDir); + + promises.push( + fs.copyFile(varsPath, path.join(distDir, theme, "tokens.css")), + ); + } + + await Promise.all(promises); +}; + +void (async () => { + console.time("copy-css-vars"); + await copyCssVars(); + console.timeEnd("copy-css-vars"); +})(); +/* eslint-enable no-console */ diff --git a/packages/theme/scripts/generate.ts b/packages/theme/scripts/generate.ts new file mode 100644 index 00000000..cc6053c0 --- /dev/null +++ b/packages/theme/scripts/generate.ts @@ -0,0 +1,98 @@ +/* eslint-disable no-console */ +import globAsync from "fast-glob"; +import { exec } from "node:child_process"; +import * as fs from "node:fs/promises"; +import * as path from "node:path"; +import { promisify } from "node:util"; +import { getFileMeta } from "../../../scripts/utils.ts"; +import type { Tokens } from "../src/types.ts"; + +type ModuleWithDefaultExport = { + default: T; +}; + +const execCmd = promisify(exec); + +const { dirname } = getFileMeta(import.meta.url); + +const packageDir = path.resolve(dirname, ".."); +const srcDir = path.join(packageDir, "src"); +const tokensGlobPath = path.join(srcDir, "**/tokens.ts"); + +const generateVar = ( + entries: Array<[PropertyKey, unknown]>, + result: string[] = [], + varPath: PropertyKey[] = [], +): string[] => { + for (const entry of entries) { + const [key, val] = entry; + + const hasPrimitiveVal = typeof val === "number" || typeof val === "string"; + + varPath.push(key); + + if (hasPrimitiveVal) { + result.push(`--tapsi-${varPath.join("-")}: ${val};`); + } else if (val !== null && typeof val === "object") { + generateVar(Object.entries(val), result, varPath); + } + + varPath.pop(); + } + + return result; +}; + +const generateVars = async (tokensPath: string, varsPath: string) => { + await execCmd(["shx", "rm", "-rf", varsPath].join(" ")); + + let tokensModule: ModuleWithDefaultExport | null = null; + + try { + tokensModule = (await import( + tokensPath + )) as ModuleWithDefaultExport; + } catch (err) { + console.error(`Couldn't resolve module at ${tokensPath}.`, { err }); + + return Promise.resolve(); + } + + const { default: tokens } = tokensModule; + + const vars = [ + ":root {", + generateVar(Object.entries(tokens)).join("\n"), + "}", + ].join("\n"); + + await fs.writeFile(varsPath, vars, { encoding: "utf-8" }); + await execCmd(`prettier ${varsPath} --write --fix`); +}; + +const generate = async () => { + const tokensFiles = await globAsync(tokensGlobPath); + const promises: Promise[] = []; + + for (const tokensFile of tokensFiles) { + const tokensPath = path.normalize(tokensFile); + const themeDir = path.dirname(tokensPath); + const theme = path.basename(themeDir); + const varsPath = path.join(themeDir, "tokens.css"); + + console.log(`🧩 generating variables for ${theme}...`); + + promises.push(generateVars(tokensPath, varsPath)); + } + + await Promise.all(promises); + + console.log("✅ generation completed."); +}; + +void (async () => { + console.time("generate"); + await generate(); + console.timeEnd("generate"); +})(); +/* eslint-enable no-console */ diff --git a/packages/theme/src/default-theme/tokens.css b/packages/theme/src/default-theme/tokens.css new file mode 100644 index 00000000..a7742b41 --- /dev/null +++ b/packages/theme/src/default-theme/tokens.css @@ -0,0 +1,216 @@ +:root { + --tapsi-palette-black: #000000; + --tapsi-palette-white: #ffffff; + --tapsi-palette-gray-50: #f5f7f7; + --tapsi-palette-gray-100: #eaeded; + --tapsi-palette-gray-200: #e1e3e3; + --tapsi-palette-gray-300: #cacccc; + --tapsi-palette-gray-400: #b1b2b2; + --tapsi-palette-gray-500: #747575; + --tapsi-palette-gray-600: #535454; + --tapsi-palette-gray-700: #323333; + --tapsi-palette-gray-800: #1f1f1f; + --tapsi-palette-gray-900: #141414; + --tapsi-palette-orange-50: #fff0e9; + --tapsi-palette-orange-100: #ffd5c2; + --tapsi-palette-orange-200: #ffac8a; + --tapsi-palette-orange-300: #ff8c61; + --tapsi-palette-orange-400: #ff7140; + --tapsi-palette-orange-500: #e55c2e; + --tapsi-palette-orange-600: #a64221; + --tapsi-palette-orange-700: #662a14; + --tapsi-palette-orange-800: #000000; + --tapsi-palette-orange-900: #422e28; + --tapsi-palette-blue-50: #eff3fe; + --tapsi-palette-blue-100: #d4e2fc; + --tapsi-palette-blue-200: #a0bff8; + --tapsi-palette-blue-300: #5b91f5; + --tapsi-palette-blue-400: #276ef1; + --tapsi-palette-blue-500: #1e54b7; + --tapsi-palette-blue-600: #174291; + --tapsi-palette-blue-700: #143166; + --tapsi-palette-blue-800: #000000; + --tapsi-palette-blue-900: #262f40; + --tapsi-palette-green-50: #e6f2ed; + --tapsi-palette-green-100: #addec9; + --tapsi-palette-green-200: #66d19e; + --tapsi-palette-green-300: #06c167; + --tapsi-palette-green-400: #048848; + --tapsi-palette-green-500: #03703c; + --tapsi-palette-green-600: #03582f; + --tapsi-palette-green-700: #10462d; + --tapsi-palette-green-800: #000000; + --tapsi-palette-green-900: #24332c; + --tapsi-palette-yellow-50: #fffaf0; + --tapsi-palette-yellow-100: #fff2d9; + --tapsi-palette-yellow-200: #ffe3ac; + --tapsi-palette-yellow-300: #ffcf70; + --tapsi-palette-yellow-400: #ffc043; + --tapsi-palette-yellow-500: #e49e15; + --tapsi-palette-yellow-600: #996f00; + --tapsi-palette-yellow-700: #674d1b; + --tapsi-palette-yellow-800: #000000; + --tapsi-palette-yellow-900: #332e24; + --tapsi-palette-red-50: #ffefed; + --tapsi-palette-red-100: #fed7d2; + --tapsi-palette-red-200: #f1998e; + --tapsi-palette-red-300: #e85c4a; + --tapsi-palette-red-400: #e11900; + --tapsi-palette-red-500: #ab1300; + --tapsi-palette-red-600: #870f00; + --tapsi-palette-red-700: #5a0a00; + --tapsi-palette-red-800: #000000; + --tapsi-palette-red-900: #402926; + --tapsi-palette-purple-50: #ffefed; + --tapsi-palette-purple-100: #fed7d2; + --tapsi-palette-purple-200: #f1998e; + --tapsi-palette-purple-300: #e85c4a; + --tapsi-palette-purple-400: #e11900; + --tapsi-palette-purple-500: #ab1300; + --tapsi-palette-purple-600: #870f00; + --tapsi-palette-purple-700: #5a0a00; + --tapsi-palette-purple-800: #000000; + --tapsi-palette-purple-900: #402926; + --tapsi-colors-brand: var(--tapsi-palette-orange-400); + --tapsi-colors-gradient-brand: linear-gradient( + 91.39deg, + #ff7733 0%, + #ff5722 50.15%, + #e64917 100% + ); + --tapsi-colors-surface-black: var(--tapsi-palette-black); + --tapsi-colors-surface-white: var(--tapsi-palette-white); + --tapsi-colors-surface-primary: var(--tapsi-palette-white); + --tapsi-colors-surface-secondary: var(--tapsi-palette-gray-50); + --tapsi-colors-surface-tertiary: var(--tapsi-palette-gray-100); + --tapsi-colors-surface-disabled: var(--tapsi-palette-gray-50); + --tapsi-colors-surface-accent: var(--tapsi-palette-blue-400); + --tapsi-colors-surface-accent-light: var(--tapsi-palette-blue-50); + --tapsi-colors-surface-negative: var(--tapsi-palette-red-400); + --tapsi-colors-surface-negative-light: var(--tapsi-palette-red-50); + --tapsi-colors-surface-warning: var(--tapsi-palette-yellow-400); + --tapsi-colors-surface-warning-light: var(--tapsi-palette-yellow-50); + --tapsi-colors-surface-positive: var(--tapsi-palette-green-400); + --tapsi-colors-surface-positive-light: var(--tapsi-palette-green-50); + --tapsi-colors-surface-background-primary: var(--tapsi-palette-gray-50); + --tapsi-colors-surface-background-secondary: var(--tapsi-palette-gray-100); + --tapsi-colors-surface-inverse-primary: var(--tapsi-palette-black); + --tapsi-colors-surface-inverse-secondary: var(--tapsi-palette-gray-700); + --tapsi-colors-surface-overlay-dark: #0000004d; + --tapsi-colors-surface-overlay-light: #0000001f; + --tapsi-colors-content-primary: var(--tapsi-palette-black); + --tapsi-colors-content-secondary: var(--tapsi-palette-gray-600); + --tapsi-colors-content-tertiary: var(--tapsi-palette-gray-500); + --tapsi-colors-content-disabled: var(--tapsi-palette-gray-400); + --tapsi-colors-content-accent: var(--tapsi-palette-blue-400); + --tapsi-colors-content-negative: var(--tapsi-palette-red-400); + --tapsi-colors-content-warning: var(--tapsi-palette-yellow-500); + --tapsi-colors-content-positive: var(--tapsi-palette-green-400); + --tapsi-colors-content-on-inverse: var(--tapsi-palette-white); + --tapsi-colors-content-on-brand: var(--tapsi-palette-white); + --tapsi-colors-content-on-accent: var(--tapsi-palette-white); + --tapsi-colors-content-on-negative: var(--tapsi-palette-white); + --tapsi-colors-content-on-warning: var(--tapsi-palette-black); + --tapsi-colors-content-on-positive: var(--tapsi-palette-white); + --tapsi-colors-border-primary: var(--tapsi-palette-gray-200); + --tapsi-colors-border-selected: var(--tapsi-palette-gray-700); + --tapsi-colors-border-focus: var(--tapsi-palette-gray-700); + --tapsi-colors-border-accent: var(--tapsi-palette-blue-200); + --tapsi-colors-border-negative: var(--tapsi-palette-red-200); + --tapsi-colors-border-positive: var(--tapsi-palette-green-200); + --tapsi-colors-border-warning: var(--tapsi-palette-yellow-200); + --tapsi-colors-border-inverse-primary: var(--tapsi-palette-gray-700); + --tapsi-radius-0: 0; + --tapsi-radius-1: 0.125rem; + --tapsi-radius-2: 0.25rem; + --tapsi-radius-3: 0.5rem; + --tapsi-radius-4: 0.75rem; + --tapsi-radius-5: 1rem; + --tapsi-radius-6: 1.25rem; + --tapsi-radius-7: 1.5rem; + --tapsi-radius-full: 999px; + --tapsi-spacing-0: 0; + --tapsi-spacing-1: 0.0625rem; + --tapsi-spacing-2: 0.125rem; + --tapsi-spacing-3: 0.25rem; + --tapsi-spacing-4: 0.5rem; + --tapsi-spacing-5: 0.75rem; + --tapsi-spacing-6: 1rem; + --tapsi-spacing-7: 1.25rem; + --tapsi-spacing-8: 1.5rem; + --tapsi-spacing-9: 2rem; + --tapsi-spacing-10: 2.5rem; + --tapsi-spacing-11: 3rem; + --tapsi-spacing-12: 4rem; + --tapsi-spacing-13: 5rem; + --tapsi-spacing-14: 6rem; + --tapsi-spacing-3-1: 0.375rem; + --tapsi-strokes-0: 0px; + --tapsi-strokes-1: 1px; + --tapsi-strokes-2: 2px; + --tapsi-typography-font-family: "Vazirmatn", sans-serif; + --tapsi-typography-body-xs-font: var(--tapsi-font-family); + --tapsi-typography-body-xs-size: 0.75rem; + --tapsi-typography-body-xs-height: 1.6666666667; + --tapsi-typography-body-xs-weight: 400; + --tapsi-typography-body-sm-font: var(--tapsi-font-family); + --tapsi-typography-body-sm-size: 0.875rem; + --tapsi-typography-body-sm-height: 1.7142857143; + --tapsi-typography-body-sm-weight: 400; + --tapsi-typography-body-md-font: var(--tapsi-font-family); + --tapsi-typography-body-md-size: 1rem; + --tapsi-typography-body-md-height: 1.75; + --tapsi-typography-body-md-weight: 400; + --tapsi-typography-body-lg-font: var(--tapsi-font-family); + --tapsi-typography-body-lg-size: 1.125rem; + --tapsi-typography-body-lg-height: 1.7777777778; + --tapsi-typography-body-lg-weight: 400; + --tapsi-typography-label-xxs-font: var(--tapsi-font-family); + --tapsi-typography-label-xxs-size: 0.625rem; + --tapsi-typography-label-xxs-height: 1.6; + --tapsi-typography-label-xxs-weight: 500; + --tapsi-typography-label-xs-font: var(--tapsi-font-family); + --tapsi-typography-label-xs-size: 0.75rem; + --tapsi-typography-label-xs-height: 2; + --tapsi-typography-label-xs-weight: 500; + --tapsi-typography-label-sm-font: var(--tapsi-font-family); + --tapsi-typography-label-sm-size: 0.875rem; + --tapsi-typography-label-sm-height: 1.7142857143; + --tapsi-typography-label-sm-weight: 500; + --tapsi-typography-label-md-font: var(--tapsi-font-family); + --tapsi-typography-label-md-size: 1rem; + --tapsi-typography-label-md-height: 1.75; + --tapsi-typography-label-md-weight: 500; + --tapsi-typography-label-lg-font: var(--tapsi-font-family); + --tapsi-typography-label-lg-size: 1.125rem; + --tapsi-typography-label-lg-height: 1.7777777778; + --tapsi-typography-label-lg-weight: 500; + --tapsi-typography-headline-xs-font: var(--tapsi-font-family); + --tapsi-typography-headline-xs-size: 1rem; + --tapsi-typography-headline-xs-height: 1.5; + --tapsi-typography-headline-xs-weight: 600; + --tapsi-typography-headline-sm-font: var(--tapsi-font-family); + --tapsi-typography-headline-sm-size: 1.25rem; + --tapsi-typography-headline-sm-height: 1.5; + --tapsi-typography-headline-sm-weight: 600; + --tapsi-typography-headline-md-font: var(--tapsi-font-family); + --tapsi-typography-headline-md-size: 1.5rem; + --tapsi-typography-headline-md-height: 1.5; + --tapsi-typography-headline-md-weight: 600; + --tapsi-typography-headline-lg-font: var(--tapsi-font-family); + --tapsi-typography-headline-lg-size: 1.75rem; + --tapsi-typography-headline-lg-height: 1.5; + --tapsi-typography-headline-lg-weight: 600; + --tapsi-typography-display-sm-font: var(--tapsi-font-family); + --tapsi-typography-display-sm-size: 2rem; + --tapsi-typography-display-sm-height: 1.5; + --tapsi-typography-display-sm-weight: 600; + --tapsi-typography-display-md-font: var(--tapsi-font-family); + --tapsi-typography-display-md-size: 2.5rem; + --tapsi-typography-display-md-height: 1.5; + --tapsi-typography-display-md-weight: 600; + --tapsi-typography-display-lg-font: var(--tapsi-font-family); + --tapsi-typography-display-lg-size: 3rem; + --tapsi-typography-display-lg-height: 1.5; + --tapsi-typography-display-lg-weight: 600; +} diff --git a/packages/theme/src/default-theme/tokens.ts b/packages/theme/src/default-theme/tokens.ts new file mode 100644 index 00000000..aad5f2c8 --- /dev/null +++ b/packages/theme/src/default-theme/tokens.ts @@ -0,0 +1,309 @@ +import type { + Colors, + Palette, + Radius, + Spacing, + Strokes, + Tokens, + Typography, +} from "../types"; + +const palette = { + black: "#000000", + white: "#ffffff", + gray: { + "900": "#141414", + "800": "#1f1f1f", + "700": "#323333", + "600": "#535454", + "500": "#747575", + "400": "#b1b2b2", + "300": "#cacccc", + "200": "#e1e3e3", + "100": "#eaeded", + "50": "#f5f7f7", + }, + orange: { + "900": "#422e28", + "800": "#000000", + "700": "#662a14", + "600": "#a64221", + "500": "#e55c2e", + "400": "#ff7140", + "300": "#ff8c61", + "200": "#ffac8a", + "100": "#ffd5c2", + "50": "#fff0e9", + }, + blue: { + "900": "#262f40", + "800": "#000000", + "700": "#143166", + "600": "#174291", + "500": "#1e54b7", + "400": "#276ef1", + "300": "#5b91f5", + "200": "#a0bff8", + "100": "#d4e2fc", + "50": "#eff3fe", + }, + green: { + "900": "#24332c", + "800": "#000000", + "700": "#10462d", + "600": "#03582f", + "500": "#03703c", + "400": "#048848", + "300": "#06c167", + "200": "#66d19e", + "100": "#addec9", + "50": "#e6f2ed", + }, + yellow: { + "900": "#332e24", + "800": "#000000", + "700": "#674d1b", + "600": "#996f00", + "500": "#e49e15", + "400": "#ffc043", + "300": "#ffcf70", + "200": "#ffe3ac", + "100": "#fff2d9", + "50": "#fffaf0", + }, + red: { + "900": "#402926", + "800": "#000000", + "700": "#5a0a00", + "600": "#870f00", + "500": "#ab1300", + "400": "#e11900", + "300": "#e85c4a", + "200": "#f1998e", + "100": "#fed7d2", + "50": "#ffefed", + }, + purple: { + "900": "#402926", + "800": "#000000", + "700": "#5a0a00", + "600": "#870f00", + "500": "#ab1300", + "400": "#e11900", + "300": "#e85c4a", + "200": "#f1998e", + "100": "#fed7d2", + "50": "#ffefed", + }, +} as const satisfies Palette; + +const colors = { + brand: "var(--tapsi-palette-orange-400)", + gradient: { + brand: "linear-gradient(91.39deg,#ff7733 0%,#ff5722 50.15%,#e64917 100%)", + }, + surface: { + black: "var(--tapsi-palette-black)", + white: "var(--tapsi-palette-white)", + primary: "var(--tapsi-palette-white)", + secondary: "var(--tapsi-palette-gray-50)", + tertiary: "var(--tapsi-palette-gray-100)", + disabled: "var(--tapsi-palette-gray-50)", + accent: "var(--tapsi-palette-blue-400)", + "accent-light": "var(--tapsi-palette-blue-50)", + negative: "var(--tapsi-palette-red-400)", + "negative-light": "var(--tapsi-palette-red-50)", + warning: "var(--tapsi-palette-yellow-400)", + "warning-light": "var(--tapsi-palette-yellow-50)", + positive: "var(--tapsi-palette-green-400)", + "positive-light": "var(--tapsi-palette-green-50)", + "background-primary": "var(--tapsi-palette-gray-50)", + "background-secondary": "var(--tapsi-palette-gray-100)", + "inverse-primary": "var(--tapsi-palette-black)", + "inverse-secondary": "var(--tapsi-palette-gray-700)", + "overlay-dark": "#0000004d", + "overlay-light": "#0000001f", + }, + content: { + primary: "var(--tapsi-palette-black)", + secondary: "var(--tapsi-palette-gray-600)", + tertiary: "var(--tapsi-palette-gray-500)", + disabled: "var(--tapsi-palette-gray-400)", + accent: "var(--tapsi-palette-blue-400)", + negative: "var(--tapsi-palette-red-400)", + warning: "var(--tapsi-palette-yellow-500)", + positive: "var(--tapsi-palette-green-400)", + "on-inverse": "var(--tapsi-palette-white)", + "on-brand": "var(--tapsi-palette-white)", + "on-accent": "var(--tapsi-palette-white)", + "on-negative": "var(--tapsi-palette-white)", + "on-warning": "var(--tapsi-palette-black)", + "on-positive": "var(--tapsi-palette-white)", + }, + border: { + primary: "var(--tapsi-palette-gray-200)", + selected: "var(--tapsi-palette-gray-700)", + focus: "var(--tapsi-palette-gray-700)", + accent: "var(--tapsi-palette-blue-200)", + negative: "var(--tapsi-palette-red-200)", + positive: "var(--tapsi-palette-green-200)", + warning: "var(--tapsi-palette-yellow-200)", + "inverse-primary": "var(--tapsi-palette-gray-700)", + }, +} as const satisfies Colors; + +const radius = { + "0": "0", + "1": "0.125rem", + "2": "0.25rem", + "3": "0.5rem", + "4": "0.75rem", + "5": "1rem", + "6": "1.25rem", + "7": "1.5rem", + full: "999px", +} as const satisfies Radius; + +const spacing = { + "0": "0", + "1": "0.0625rem", + "2": "0.125rem", + "3": "0.25rem", + "3-1": "0.375rem", + "4": "0.5rem", + "5": "0.75rem", + "6": "1rem", + "7": "1.25rem", + "8": "1.5rem", + "9": "2rem", + "10": "2.5rem", + "11": "3rem", + "12": "4rem", + "13": "5rem", + "14": "6rem", +} as const satisfies Spacing; + +const strokes = { + "0": "0px", + "1": "1px", + "2": "2px", +} as const satisfies Strokes; + +const typography = { + "font-family": '"Vazirmatn", sans-serif', + body: { + xs: { + font: "var(--tapsi-font-family)", + size: "0.75rem", + height: 1.6666666667, + weight: 400, + }, + sm: { + font: "var(--tapsi-font-family)", + size: "0.875rem", + height: 1.7142857143, + weight: 400, + }, + md: { + font: "var(--tapsi-font-family)", + size: "1rem", + height: 1.75, + weight: 400, + }, + lg: { + font: "var(--tapsi-font-family)", + size: "1.125rem", + height: 1.7777777778, + weight: 400, + }, + }, + label: { + xxs: { + font: "var(--tapsi-font-family)", + size: "0.625rem", + height: 1.6, + weight: 500, + }, + xs: { + font: "var(--tapsi-font-family)", + size: "0.75rem", + height: 2, + weight: 500, + }, + sm: { + font: "var(--tapsi-font-family)", + size: "0.875rem", + height: 1.7142857143, + weight: 500, + }, + md: { + font: "var(--tapsi-font-family)", + size: "1rem", + height: 1.75, + weight: 500, + }, + lg: { + font: "var(--tapsi-font-family)", + size: "1.125rem", + height: 1.7777777778, + weight: 500, + }, + }, + headline: { + xs: { + font: "var(--tapsi-font-family)", + size: "1rem", + height: 1.5, + weight: 600, + }, + sm: { + font: "var(--tapsi-font-family)", + size: "1.25rem", + height: 1.5, + weight: 600, + }, + md: { + font: "var(--tapsi-font-family)", + size: "1.5rem", + height: 1.5, + weight: 600, + }, + lg: { + font: "var(--tapsi-font-family)", + size: "1.75rem", + height: 1.5, + weight: 600, + }, + }, + display: { + sm: { + font: "var(--tapsi-font-family)", + size: "2rem", + height: 1.5, + weight: 600, + }, + md: { + font: "var(--tapsi-font-family)", + size: "2.5rem", + height: 1.5, + weight: 600, + }, + lg: { + font: "var(--tapsi-font-family)", + size: "3rem", + height: 1.5, + weight: 600, + }, + }, +} as const satisfies Typography; + +const tokens = { + palette, + colors, + radius, + spacing, + strokes, + typography, +} as const satisfies Tokens; + +export default tokens; diff --git a/packages/theme/src/index.css b/packages/theme/src/index.css deleted file mode 100644 index ea781868..00000000 --- a/packages/theme/src/index.css +++ /dev/null @@ -1,6 +0,0 @@ -@import "tokens/palette.css"; -@import "tokens/colors.css"; -@import "tokens/radius.css"; -@import "tokens/spacing.css"; -@import "tokens/strokes.css"; -@import "tokens/typography.css"; diff --git a/packages/theme/src/tokens/colors.css b/packages/theme/src/tokens/colors.css deleted file mode 100644 index d6cdb4b9..00000000 --- a/packages/theme/src/tokens/colors.css +++ /dev/null @@ -1,67 +0,0 @@ -:root { - /* surface */ - --tapsi-color-surface-primary: var(--tapsi-palette-white); - --tapsi-color-surface-secondary: var(--tapsi-palette-gray-50); - --tapsi-color-surface-tertiary: var(--tapsi-palette-gray-100); - --tapsi-color-surface-background-primary: var(--tapsi-palette-gray-50); - --tapsi-color-surface-background-secondary: var(--tapsi-palette-gray-100); - --tapsi-color-surface-disabled: var(--tapsi-palette-gray-50); - --tapsi-color-surface-inverse-primary: var(--tapsi-palette-black); - --tapsi-color-surface-inverse-secondary: var(--tapsi-palette-gray-700); - - --tapsi-color-surface-accent: var(--tapsi-palette-blue-400); - --tapsi-color-surface-negative: var(--tapsi-palette-red-400); - --tapsi-color-surface-warning: var(--tapsi-palette-yellow-400); - --tapsi-color-surface-positive: var(--tapsi-palette-green-400); - - --tapsi-color-surface-accent-light: var(--tapsi-palette-blue-50); - --tapsi-color-surface-negative-light: var(--tapsi-palette-red-50); - --tapsi-color-surface-warning-light: var(--tapsi-palette-yellow-50); - --tapsi-color-surface-positive-light: var(--tapsi-palette-green-50); - - --tapsi-color-surface-overlay-dark: #0000004d; - --tapsi-color-surface-overlay-light: #0000001f; - - --tapsi-color-surface-black: var(--tapsi-palette-black); - --tapsi-color-surface-white: var(--tapsi-palette-white); - - /* content */ - --tapsi-color-content-primary: var(--tapsi-palette-black); - --tapsi-color-content-secondary: var(--tapsi-palette-gray-600); - --tapsi-color-content-tertiary: var(--tapsi-palette-gray-500); - --tapsi-color-content-disabled: var(--tapsi-palette-gray-400); - --tapsi-color-content-on-inverse: var(--tapsi-palette-white); - - --tapsi-color-content-accent: var(--tapsi-palette-blue-400); - --tapsi-color-content-negative: var(--tapsi-palette-red-400); - --tapsi-color-content-warning: var(--tapsi-palette-yellow-500); - --tapsi-color-content-positive: var(--tapsi-palette-green-400); - - --tapsi-color-content-on-brand: var(--tapsi-palette-white); - --tapsi-color-content-on-accent: var(--tapsi-palette-white); - --tapsi-color-content-on-negative: var(--tapsi-palette-white); - --tapsi-color-content-on-warning: var(--tapsi-palette-black); - --tapsi-color-content-on-postive: var(--tapsi-palette-white); - - /* border */ - --tapsi-color-border-primary: var(--tapsi-palette-gray-200); - --tapsi-color-border-inverse-primary: var(--tapsi-palette-gray-700); - --tapsi-color-border-selected: var(--tapsi-palette-gray-700); - --tapsi-color-border-focus: var(--tapsi-palette-gray-700); - - --tapsi-color-border-accent: var(--tapsi-palette-blue-200); - --tapsi-color-border-negative: var(--tapsi-palette-red-200); - --tapsi-color-border-postive: var(--tapsi-palette-green-200); - --tapsi-color-border-warning: var(--tapsi-palette-yellow-200); - - /* brand */ - --tapsi-color-brand: var(--tapsi-palette-orange-400); - - /* gradient */ - --tapsi-color-gradient-brand: linear-gradient( - 91.39deg, - #ff7733 0%, - #ff5722 50.15%, - #e64917 100% - ); -} diff --git a/packages/theme/src/tokens/palette.css b/packages/theme/src/tokens/palette.css deleted file mode 100644 index 98f4a545..00000000 --- a/packages/theme/src/tokens/palette.css +++ /dev/null @@ -1,81 +0,0 @@ -:root { - --tapsi-palette-black: #000000; - --tapsi-palette-white: #ffffff; - - --tapsi-palette-gray-900: #141414; - --tapsi-palette-gray-800: #1f1f1f; - --tapsi-palette-gray-700: #323333; - --tapsi-palette-gray-600: #535454; - --tapsi-palette-gray-500: #747575; - --tapsi-palette-gray-400: #b1b2b2; - --tapsi-palette-gray-300: #cacccc; - --tapsi-palette-gray-200: #e1e3e3; - --tapsi-palette-gray-100: #eaeded; - --tapsi-palette-gray-50: #f5f7f7; - - --tapsi-palette-orange-900: #422e28; - --tapsi-palette-orange-800: #000000; - --tapsi-palette-orange-700: #662a14; - --tapsi-palette-orange-600: #a64221; - --tapsi-palette-orange-500: #e55c2e; - --tapsi-palette-orange-400: #ff7140; - --tapsi-palette-orange-300: #ff8c61; - --tapsi-palette-orange-200: #ffac8a; - --tapsi-palette-orange-100: #ffd5c2; - --tapsi-palette-orange-50: #fff0e9; - - --tapsi-palette-blue-900: #262f40; - --tapsi-palette-blue-800: #000000; - --tapsi-palette-blue-700: #143166; - --tapsi-palette-blue-600: #174291; - --tapsi-palette-blue-500: #1e54b7; - --tapsi-palette-blue-400: #276ef1; - --tapsi-palette-blue-300: #5b91f5; - --tapsi-palette-blue-200: #a0bff8; - --tapsi-palette-blue-100: #d4e2fc; - --tapsi-palette-blue-50: #eff3fe; - - --tapsi-palette-green-900: #24332c; - --tapsi-palette-green-800: #000000; - --tapsi-palette-green-700: #10462d; - --tapsi-palette-green-600: #03582f; - --tapsi-palette-green-500: #03703c; - --tapsi-palette-green-400: #048848; - --tapsi-palette-green-300: #06c167; - --tapsi-palette-green-200: #66d19e; - --tapsi-palette-green-100: #addec9; - --tapsi-palette-green-50: #e6f2ed; - - --tapsi-palette-yellow-900: #332e24; - --tapsi-palette-yellow-800: #000000; - --tapsi-palette-yellow-700: #674d1b; - --tapsi-palette-yellow-600: #996f00; - --tapsi-palette-yellow-500: #e49e15; - --tapsi-palette-yellow-400: #ffc043; - --tapsi-palette-yellow-300: #ffcf70; - --tapsi-palette-yellow-200: #ffe3ac; - --tapsi-palette-yellow-100: #fff2d9; - --tapsi-palette-yellow-50: #fffaf0; - - --tapsi-palette-red-900: #402926; - --tapsi-palette-red-800: #000000; - --tapsi-palette-red-700: #5a0a00; - --tapsi-palette-red-600: #870f00; - --tapsi-palette-red-500: #ab1300; - --tapsi-palette-red-400: #e11900; - --tapsi-palette-red-300: #e85c4a; - --tapsi-palette-red-200: #f1998e; - --tapsi-palette-red-100: #fed7d2; - --tapsi-palette-red-50: #ffefed; - - --tapsi-palette-purple-900: #402926; - --tapsi-palette-purple-800: #000000; - --tapsi-palette-purple-700: #5a0a00; - --tapsi-palette-purple-600: #870f00; - --tapsi-palette-purple-500: #ab1300; - --tapsi-palette-purple-400: #e11900; - --tapsi-palette-purple-300: #e85c4a; - --tapsi-palette-purple-200: #f1998e; - --tapsi-palette-purple-100: #fed7d2; - --tapsi-palette-purple-50: #ffefed; -} diff --git a/packages/theme/src/tokens/radius.css b/packages/theme/src/tokens/radius.css deleted file mode 100644 index 4612fb72..00000000 --- a/packages/theme/src/tokens/radius.css +++ /dev/null @@ -1,11 +0,0 @@ -:root { - --tapsi-radius-0: 0; - --tapsi-radius-1: 0.125rem; - --tapsi-radius-2: 0.25rem; - --tapsi-radius-3: 0.5rem; - --tapsi-radius-4: 0.75rem; - --tapsi-radius-5: 1rem; - --tapsi-radius-6: 1.25rem; - --tapsi-radius-7: 1.5rem; - --tapsi-radius-full: 999px; -} diff --git a/packages/theme/src/tokens/spacing.css b/packages/theme/src/tokens/spacing.css deleted file mode 100644 index 71c06e61..00000000 --- a/packages/theme/src/tokens/spacing.css +++ /dev/null @@ -1,18 +0,0 @@ -:root { - --tapsi-spacing-0: 0; - --tapsi-spacing-1: 0.0625rem; - --tapsi-spacing-2: 0.125rem; - --tapsi-spacing-3: 0.25rem; - --tapsi-spacing-3-1: 0.375rem; - --tapsi-spacing-4: 0.5rem; - --tapsi-spacing-5: 0.75rem; - --tapsi-spacing-6: 1rem; - --tapsi-spacing-7: 1.25rem; - --tapsi-spacing-8: 1.5rem; - --tapsi-spacing-9: 2rem; - --tapsi-spacing-10: 2.5rem; - --tapsi-spacing-11: 3rem; - --tapsi-spacing-12: 4rem; - --tapsi-spacing-13: 5rem; - --tapsi-spacing-14: 6rem; -} diff --git a/packages/theme/src/tokens/strokes.css b/packages/theme/src/tokens/strokes.css deleted file mode 100644 index b3e1647d..00000000 --- a/packages/theme/src/tokens/strokes.css +++ /dev/null @@ -1,5 +0,0 @@ -:root { - --tapsi-stroke-0: 0px; - --tapsi-stroke-1: 1px; - --tapsi-stroke-2: 2px; -} diff --git a/packages/theme/src/tokens/typography.css b/packages/theme/src/tokens/typography.css deleted file mode 100644 index 30da8ab8..00000000 --- a/packages/theme/src/tokens/typography.css +++ /dev/null @@ -1,88 +0,0 @@ -:root { - /* family */ - --tapsi-font-family: "Vazirmatn", sans-serif; - - /* body */ - --tapsi-typography-body-xs-font: var(--tapsi-font-family); - --tapsi-typography-body-xs-size: 0.75rem; - --tapsi-typography-body-xs-height: 1.6666666667; - --tapsi-typography-body-xs-weight: 400; - - --tapsi-typography-body-sm-font: var(--tapsi-font-family); - --tapsi-typography-body-sm-size: 0.875rem; - --tapsi-typography-body-sm-height: 1.7142857143; - --tapsi-typography-body-sm-weight: 400; - - --tapsi-typography-body-md-font: var(--tapsi-font-family); - --tapsi-typography-body-md-size: 1rem; - --tapsi-typography-body-md-height: 1.75; - --tapsi-typography-body-md-weight: 400; - - --tapsi-typography-body-lg-font: var(--tapsi-font-family); - --tapsi-typography-body-lg-size: 1.125rem; - --tapsi-typography-body-lg-height: 1.7777777778; - --tapsi-typography-body-lg-weight: 400; - - /* label */ - --tapsi-typography-label-xxs-font: var(--tapsi-font-family); - --tapsi-typography-label-xxs-size: 0.625rem; - --tapsi-typography-label-xxs-height: 1.6; - --tapsi-typography-label-xxs-weight: 500; - - --tapsi-typography-label-xs-font: var(--tapsi-font-family); - --tapsi-typography-label-xs-size: 0.75rem; - --tapsi-typography-label-xs-height: 2; - --tapsi-typography-label-xs-weight: 500; - - --tapsi-typography-label-sm-font: var(--tapsi-font-family); - --tapsi-typography-label-sm-size: 0.875rem; - --tapsi-typography-label-sm-height: 1.7142857143; - --tapsi-typography-label-sm-weight: 500; - - --tapsi-typography-label-md-font: var(--tapsi-font-family); - --tapsi-typography-label-md-size: 1rem; - --tapsi-typography-label-md-height: 1.75; - --tapsi-typography-label-md-weight: 500; - - --tapsi-typography-label-lg-font: var(--tapsi-font-family); - --tapsi-typography-label-lg-size: 1.125rem; - --tapsi-typography-label-lg-height: 1.7777777778; - --tapsi-typography-label-lg-weight: 500; - - /* headline */ - --tapsi-typography-headline-xs-font: var(--tapsi-font-family); - --tapsi-typography-headline-xs-size: 1rem; - --tapsi-typography-headline-xs-height: 1.5; - --tapsi-typography-headline-xs-weight: 600; - - --tapsi-typography-headline-sm-font: var(--tapsi-font-family); - --tapsi-typography-headline-sm-size: 1.25rem; - --tapsi-typography-headline-sm-height: 1.5; - --tapsi-typography-headline-sm-weight: 600; - - --tapsi-typography-headline-md-font: var(--tapsi-font-family); - --tapsi-typography-headline-md-size: 1.5rem; - --tapsi-typography-headline-md-height: 1.5; - --tapsi-typography-headline-md-weight: 600; - - --tapsi-typography-headline-lg-font: var(--tapsi-font-family); - --tapsi-typography-headline-lg-size: 1.75rem; - --tapsi-typography-headline-lg-height: 1.5; - --tapsi-typography-headline-lg-weight: 600; - - /* display */ - --tapsi-typography-display-sm-font: var(--tapsi-font-family); - --tapsi-typography-display-sm-size: 2rem; - --tapsi-typography-display-sm-height: 1.5; - --tapsi-typography-display-sm-weight: 600; - - --tapsi-typography-display-md-font: var(--tapsi-font-family); - --tapsi-typography-display-md-size: 2.5rem; - --tapsi-typography-display-md-height: 1.5; - --tapsi-typography-display-md-weight: 600; - - --tapsi-typography-display-lg-font: var(--tapsi-font-family); - --tapsi-typography-display-lg-size: 3rem; - --tapsi-typography-display-lg-height: 1.5; - --tapsi-typography-display-lg-weight: 600; -} diff --git a/packages/theme/src/types.ts b/packages/theme/src/types.ts new file mode 100644 index 00000000..f1917d8a --- /dev/null +++ b/packages/theme/src/types.ts @@ -0,0 +1,130 @@ +export type ColorSetSteps = + | "50" + | "100" + | "200" + | "300" + | "400" + | "500" + | "600" + | "700" + | "800" + | "900"; + +export type Palette = { + black: string; + white: string; + gray: Record; + orange: Record; + blue: Record; + green: Record; + yellow: Record; + red: Record; + purple: Record; +}; + +export type Colors = { + brand: string; + gradient: Record<"brand", string>; + surface: Record< + | "primary" + | "secondary" + | "tertiary" + | "disabled" + | "black" + | "white" + | "accent" + | "accent-light" + | "negative" + | "negative-light" + | "warning" + | "warning-light" + | "positive" + | "positive-light" + | "background-primary" + | "background-secondary" + | "inverse-primary" + | "inverse-secondary" + | "overlay-light" + | "overlay-dark", + string + >; + content: Record< + | "primary" + | "secondary" + | "tertiary" + | "disabled" + | "accent" + | "negative" + | "warning" + | "positive" + | "on-inverse" + | "on-brand" + | "on-accent" + | "on-negative" + | "on-warning" + | "on-positive", + string + >; + border: Record< + | "primary" + | "selected" + | "focus" + | "accent" + | "negative" + | "warning" + | "positive" + | "inverse-primary", + string + >; +}; + +export type Radius = Record< + "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "full", + string +>; + +export type Spacing = Record< + | "0" + | "1" + | "2" + | "3" + | "3-1" + | "4" + | "5" + | "6" + | "7" + | "8" + | "9" + | "10" + | "11" + | "12" + | "13" + | "14", + string +>; + +export type Strokes = Record<"0" | "1" | "2", string>; + +export type TypographyVariant = { + font: string; + size: string; + height: number; + weight: number; +}; + +export type Typography = { + "font-family": string; + body: Record<"xs" | "sm" | "md" | "lg", TypographyVariant>; + label: Record<"xxs" | "xs" | "sm" | "md" | "lg", TypographyVariant>; + headline: Record<"xs" | "sm" | "md" | "lg", TypographyVariant>; + display: Record<"sm" | "md" | "lg", TypographyVariant>; +}; + +export type Tokens = { + palette: Palette; + colors: Colors; + radius: Radius; + spacing: Spacing; + strokes: Strokes; + typography: Typography; +}; diff --git a/packages/theme/tsconfig.build.json b/packages/theme/tsconfig.build.json new file mode 100644 index 00000000..f1612856 --- /dev/null +++ b/packages/theme/tsconfig.build.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src"] +} diff --git a/playground/src/index.ts b/playground/src/index.ts index f88e2b25..554c4b55 100644 --- a/playground/src/index.ts +++ b/playground/src/index.ts @@ -1,3 +1,4 @@ -import "@tapsioss/theme"; +import "@tapsioss/theme/css-variables"; + // Import modules using path aliases // i.e. `import "@tapsioss/web-components/avatar";` diff --git a/playground/src/test-setup.ts b/playground/src/test-setup.ts index e3a87318..d5e0cf27 100644 --- a/playground/src/test-setup.ts +++ b/playground/src/test-setup.ts @@ -1,4 +1,4 @@ -import "@tapsioss/theme"; +import "@tapsioss/theme/css-variables"; import "@tapsioss/web-components/avatar"; import "@tapsioss/web-components/badge"; diff --git a/tsconfig.json b/tsconfig.json index 4f9bbc21..c327359c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -56,10 +56,12 @@ "./packages/react-components/src/*" ], // Tokens - "@tapsioss/theme": [ - "./packages/theme/dist/index.css", - "./packages/theme/src/index.css" - ] + "@tapsioss/theme/*/css-variables": ["./packages/theme/src/*/tokens.css"], + "@tapsioss/theme/css-variables": [ + "./packages/theme/src/default-theme/tokens.css" + ], + "@tapsioss/theme/*/tokens": ["./packages/theme/src/*/tokens.ts"], + "@tapsioss/theme/tokens": ["./packages/theme/src/default-theme/tokens.ts"] } }, "include": ["**/*.ts", "**/*.tsx", "docs/.vitepress/**/*.ts"],