@slotted/theme 0.0.0-stage → 0.1.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,24 @@
1
+ export { contrast, deltaE, fromSrgb, parseSeed, relativeLuminance, toHex, toLinearSrgb, } from './color';
2
+ export type { Luminance, Oklch } from './color';
3
+ export { LAYER_NAMES, LAYER_ORDER_STATEMENT } from './layer-order';
4
+ export { applyBounds, boundCss, boundValue, fitBound } from './bound';
5
+ export type { Bound } from './bound';
6
+ export { derivationCss, evaluateRecipe, SCHEMES, SEED_NAMES } from './recipe';
7
+ export type { AliasDerivation, Derivation, MixDerivation, Recipe, Scheme, SeedDerivation, SeedName, } from './recipe';
8
+ export { checkScheme, CONTRAST_REQUIREMENT, seedGrid } from './recipe-checks';
9
+ export { recipes } from './recipes';
10
+ export type { TokenContract, TokenDefinition } from './contract-types';
11
+ export { DENSITIES, defineTheme, extendTheme, flattenTheme } from './define';
12
+ export type { Density, FontFaceDefinition, Theme, ThemeDefinition, ThemeOverrides } from './define';
13
+ export { COMPONENT_TOKENS, contract } from './generated/contract';
14
+ export type { BaseToken, ColorToken, ComponentToken, DensityToken, MotionToken, SchemeToken, } from './generated/contract';
15
+ export { resolveTokens } from './resolve';
16
+ export { validateContract, validateTheme } from './validate';
17
+ export type { FlatTheme } from './validate';
18
+ export { renderTheme } from './render';
19
+ export { splitTopLevel } from './rules';
20
+ export { ruleErrors } from './rule-policy';
21
+ export type { RuleError } from './rule-policy';
22
+ export { applyTheme, THEME_CHANGE_EVENT } from './apply';
23
+ export { seedStyle } from './seeds';
24
+ export type { ThemeChangeDetail } from './apply';
@@ -0,0 +1,9 @@
1
+ /**
2
+ * `slotted` is the only top-level layer. `theme-rules` holds the rules a theme
3
+ * carries, after the family stylesheets they adjust, so a theme rule wins by
4
+ * layer order and never by specificity. `reset` and `tokens` hold no rule;
5
+ * they are declared because a layer takes its position from the first
6
+ * mention of its name.
7
+ */
8
+ export declare const LAYER_NAMES: readonly ["reset", "tokens", "theme", "components", "theme-rules"];
9
+ export declare const LAYER_ORDER_STATEMENT: string;
@@ -0,0 +1,11 @@
1
+ import { type Oklch } from './color';
2
+ import type { Scheme } from './recipe';
3
+ /** The text ratio each scheme promises. `high-contrast` earns its name at AAA. */
4
+ export declare const CONTRAST_REQUIREMENT: Readonly<Record<Scheme, number>>;
5
+ /**
6
+ * The ratios of `theme-conformance` over evaluated colours, as messages. A
7
+ * recipe passes when every seed of `seedGrid()` leaves this list empty.
8
+ */
9
+ export declare function checkScheme(values: Readonly<Record<string, Oklch>>, scheme: Scheme): string[];
10
+ /** Hue 0–345 by 15, chroma 0–0.35 by 0.05, lightness 0.2–0.95 by 0.05: 3072 seeds. */
11
+ export declare function seedGrid(): Oklch[];
@@ -0,0 +1,41 @@
1
+ import { type Bound } from './bound';
2
+ import type { Oklch } from './color';
3
+ import { SEED_NAMES, type SeedName } from './seeds';
4
+ export { SEED_NAMES, type SeedName };
5
+ export declare const SCHEMES: readonly ["light", "dark", "high-contrast"];
6
+ export type Scheme = (typeof SCHEMES)[number];
7
+ /** A colour taken from a seed: fixed or offset lightness, scaled chroma, shifted hue. */
8
+ export interface SeedDerivation {
9
+ readonly seed: SeedName;
10
+ readonly l: number | {
11
+ readonly offset: number;
12
+ readonly min?: number;
13
+ readonly max?: number;
14
+ };
15
+ readonly c: {
16
+ readonly scale: number;
17
+ readonly max: number;
18
+ };
19
+ readonly h?: number;
20
+ readonly bounds?: readonly Bound[];
21
+ }
22
+ /** A colour moved `share` toward white, or toward black when its lightness is above 0.9. */
23
+ export interface MixDerivation {
24
+ readonly mix: string;
25
+ readonly share: number;
26
+ readonly bounds?: readonly Bound[];
27
+ }
28
+ export interface AliasDerivation {
29
+ readonly alias: string;
30
+ }
31
+ export type Derivation = SeedDerivation | MixDerivation | AliasDerivation;
32
+ /** Per scheme, the derivation of every colour token the recipe owns. */
33
+ export type Recipe = Readonly<Record<Scheme, Readonly<Record<string, Derivation>>>>;
34
+ /**
35
+ * Every token of one scheme, computed with the arithmetic `derivationCss`
36
+ * writes: the browser's `oklch(from …)` and `color-mix(in oklch, …)` on the
37
+ * same constants.
38
+ */
39
+ export declare function evaluateRecipe(recipe: Recipe, seeds: Readonly<Record<SeedName, Oklch>>, scheme: Scheme): Record<string, Oklch>;
40
+ /** The CSS value of one derivation. */
41
+ export declare function derivationCss(rule: Derivation): string;
@@ -0,0 +1,4 @@
1
+ import type { Recipe } from '../recipe';
2
+ export declare const recipes: {
3
+ readonly standard: Recipe;
4
+ };
@@ -0,0 +1,2 @@
1
+ import type { Recipe } from '../recipe';
2
+ export declare const standard: Recipe;
@@ -0,0 +1,6 @@
1
+ import { type Theme } from './define';
2
+ /**
3
+ * The stylesheet of one theme. The theme is checked as `defineTheme` checks
4
+ * it, and the text is safe to inline in an HTML `<style>`.
5
+ */
6
+ export declare function renderTheme(theme: Theme): string;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Expands the `var()` references a token map makes to itself. It is not a CSS
3
+ * engine: it resolves within one map, which is what one theme block is.
4
+ */
5
+ export declare function resolveTokens(values: Readonly<Record<string, string>>): Record<string, string>;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * What a theme rule may select and read (design §10.2, ADR 0004). A rule
3
+ * selects only what a binding renders on purpose — a family by `data-ui`, its
4
+ * parts by `data-part`, and the `data-*` attributes a family or the theme
5
+ * axes set — and reads only tokens. Everything else is an implementation
6
+ * detail of a family stylesheet, which changes without notice.
7
+ *
8
+ * `defineTheme` (every rule given as text) and `slotted-build-theme` (every
9
+ * rule, after lightningcss has parsed it) both call `ruleErrors`, so the two
10
+ * paths hold one policy.
11
+ */
12
+ /** One violation: the 1-based line in the rule text, the selector of the rule it sits in, and why. */
13
+ export interface RuleError {
14
+ readonly line: number;
15
+ readonly selector: string;
16
+ readonly message: string;
17
+ }
18
+ /**
19
+ * The reasons a selector list breaks the policy, each naming the part of the
20
+ * selector at fault. `nested` is true for a rule inside a style rule, where
21
+ * `&` is that rule's element.
22
+ */
23
+ export declare function selectorErrors(selector: string, nested?: boolean): string[];
24
+ /** The reason a declaration writes a property no theme may write, or `undefined`. */
25
+ export declare function propertyWriteError(name: string): string | undefined;
26
+ /**
27
+ * The reason a `var()`, or a `style()` query in a value or in `@container`,
28
+ * reads a property no theme may read, or `undefined`.
29
+ */
30
+ export declare function tokenReadError(name: string, reader?: 'var' | 'style'): string | undefined;
31
+ /**
32
+ * Every policy violation in a theme's CSS text: each style rule's selector,
33
+ * nested rules and rules in conditional groups included; every `@scope`,
34
+ * `@import` and `@layer`, at any depth; every `@property` naming a private
35
+ * property or a token; every declaration that writes a private property or
36
+ * stands outside a style rule; and every `var()` and `style()` query the text
37
+ * reads, outside strings and comments, with escapes in the function and
38
+ * property names decoded. The
39
+ * text is taken to be balanced; `splitTopLevel` refuses one that is not.
40
+ */
41
+ export declare function ruleErrors(css: string): RuleError[];
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Splits CSS into its top-level statements: a block ends at the brace that
3
+ * returns to depth 0, an at-statement at the semicolon. Strings and comments
4
+ * are skipped, so a brace inside `content: "}"` does not end a rule.
5
+ *
6
+ * A theme rule is text a theme author wrote, concatenated with every other
7
+ * rule into one `@layer`, so an unbalanced `}` here would close that layer
8
+ * (or the `@scope` around it) early and let the rest of the input escape its
9
+ * boundary. The five malformed shapes below are refused rather than silently
10
+ * reinterpreted: a closing brace with nothing open, braces still open at the
11
+ * end, a string never closed (or ended by a newline), a comment never closed,
12
+ * and a trailing `\` that would escape whatever the compiler writes next.
13
+ */
14
+ export declare function splitTopLevel(css: string): string[];
15
+ /**
16
+ * The rules of one theme, ready for the compiler: `@keyframes`, `@font-face`
17
+ * and `@property` leave the scope they cannot live in, and every keyframes
18
+ * name takes the theme's prefix so two themes on one page never collide.
19
+ * `renderTheme` calls it only on rules `ruleErrors` accepted, which refuses
20
+ * `@scope`, `@import` and `@layer` at any depth.
21
+ */
22
+ export declare function processRules(themeName: string, sources: readonly string[]): {
23
+ hoisted: string[];
24
+ body: string;
25
+ };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The seeds: the colours every derived colour token is computed from. This
3
+ * module is the entry `@slotted/theme/seeds`, which writes a reader's seeds
4
+ * without loading the contract, the validator or the renderer.
5
+ */
6
+ export declare const SEED_NAMES: readonly ["accent", "neutral", "success", "warning", "danger"];
7
+ export type SeedName = (typeof SEED_NAMES)[number];
8
+ /** The `style` object that gives an element its own seeds, for React `style` and Angular `[style]`. */
9
+ export declare function seedStyle(seeds: Readonly<Partial<Record<SeedName, string>>>): Record<string, string>;
@@ -0,0 +1,26 @@
1
+ import type { TokenContract } from './contract-types';
2
+ type Values = Record<string, string>;
3
+ /** The resolved shape of a theme: what `flattenTheme` returns and what the contract is checked on. */
4
+ export interface FlatTheme {
5
+ readonly name: string;
6
+ readonly base: Values;
7
+ readonly schemes: Readonly<Record<string, Values>>;
8
+ readonly densities: Readonly<Record<string, Values>>;
9
+ readonly reducedMotion: Values;
10
+ }
11
+ /**
12
+ * A token value, a component value or a font field is interpolated into CSS
13
+ * text verbatim (`render.ts` writes `${name}: ${value};` and the like). An
14
+ * unquoted, uncommented `;`, `{` or `}` inside it would close the
15
+ * declaration, the rule or the `@layer`/`@scope` it is written into, and a
16
+ * string or comment the value leaves open, or a trailing `\` escaping the
17
+ * character written after it, would do the same to whatever follows it in
18
+ * the stylesheet — so all of them are rejected here, and so is a value that
19
+ * is not a string, which `String()` could turn into any text. `url("a;b.woff2")`
20
+ * and `"1px /* comment *\/"` stay legitimate: the scan this shares with
21
+ * `splitTopLevel` skips a structural character inside either.
22
+ */
23
+ export declare function hasUnsafeStructuralChar(value: unknown): boolean;
24
+ export declare function validateContract(contract: TokenContract): string[];
25
+ export declare function validateTheme(contract: TokenContract, theme: Partial<FlatTheme>): string[];
26
+ export {};
package/package.json CHANGED
@@ -1,6 +1,54 @@
1
1
  {
2
2
  "name": "@slotted/theme",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0-beta.0",
4
+ "license": "MIT",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/andrelopes-code/slotted.git",
8
+ "directory": "packages/theme"
9
+ },
10
+ "type": "module",
11
+ "files": [
12
+ "dist",
13
+ "src/contract.json",
14
+ "cli",
15
+ "conformance"
16
+ ],
17
+ "bin": {
18
+ "slotted-build-theme": "./cli/build-theme.mjs"
19
+ },
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/types/index.d.ts",
23
+ "import": "./dist/index.js"
24
+ },
25
+ "./layer-order": {
26
+ "types": "./dist/types/layer-order.d.ts",
27
+ "import": "./dist/layer-order.js"
28
+ },
29
+ "./seeds": {
30
+ "types": "./dist/types/seeds.d.ts",
31
+ "import": "./dist/seeds.js"
32
+ },
33
+ "./contract.json": "./src/contract.json",
34
+ "./conformance": "./conformance/theme-conformance.mjs",
35
+ "./styles.css": "./dist/styles.css"
36
+ },
37
+ "dependencies": {
38
+ "lightningcss": "1.33.0"
39
+ },
40
+ "devDependencies": {
41
+ "@types/node": "24.13.3",
42
+ "jsdom": "30.0.1",
43
+ "typescript": "6.0.3",
44
+ "vite": "8.2.2",
45
+ "vitest": "4.1.11"
46
+ },
47
+ "scripts": {
48
+ "generate": "node scripts/generate-sources.mjs",
49
+ "build": "vite build && tsc -p tsconfig.build.json && node scripts/write-styles.mjs",
50
+ "test": "vitest run && node --test test/*.test.mjs",
51
+ "typecheck": "tsc -p tsconfig.json --noEmit --pretty false",
52
+ "verify": "pnpm build && pnpm test && pnpm typecheck"
53
+ }
6
54
  }