Skip to content

Commit 4adfd20

Browse files
committed
refactor(config): make internal properties overridable defaults
Rename INTERNAL_PROPERTIES to DEFAULT_PROPERTIES and merge user-configured properties on top so they can override defaults. Add #clear and #border color tokens. Made-with: Cursor
1 parent 5a9bc24 commit 4adfd20

File tree

5 files changed

+44
-46
lines changed

5 files changed

+44
-46
lines changed

.changeset/smooth-pens-wave.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@tenphi/tasty': patch
3+
---
4+
5+
Make internal properties overridable via `configure({ properties })` by merging user properties on top of defaults. Add `#clear` (transparent) and `#border` (rgb(0 0 0)) color tokens.

src/compute-styles.ts

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,10 @@ import {
2222
getConfig,
2323
getGlobalConfigTokens,
2424
getGlobalCounterStyle,
25+
getEffectiveProperties,
2526
getGlobalFontFace,
2627
getGlobalKeyframes,
27-
getGlobalProperties,
2828
hasGlobalKeyframes,
29-
hasGlobalProperties,
30-
INTERNAL_PROPERTIES,
3129
} from './config';
3230
import {
3331
counterStyle,
@@ -138,21 +136,11 @@ function collectInternalsRSC(rscCache: RSCStyleCache): string {
138136

139137
const parts: string[] = [];
140138

141-
for (const [token, definition] of Object.entries(INTERNAL_PROPERTIES)) {
139+
for (const [token, definition] of Object.entries(getEffectiveProperties())) {
142140
const css = formatPropertyCSS(token, definition);
143141
if (css) parts.push(css);
144142
}
145143

146-
if (hasGlobalProperties()) {
147-
const globalProps = getGlobalProperties();
148-
if (globalProps) {
149-
for (const [token, definition] of Object.entries(globalProps)) {
150-
const css = formatPropertyCSS(token, definition);
151-
if (css) parts.push(css);
152-
}
153-
}
154-
}
155-
156144
const tokenStyles = getGlobalConfigTokens();
157145
if (tokenStyles && Object.keys(tokenStyles).length > 0) {
158146
const tokenRules = renderStyles(tokenStyles, ':root') as StyleResult[];

src/config.ts

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,11 @@ export interface TastyConfig {
146146
/**
147147
* Global CSS @property definitions for custom properties.
148148
* Keys use tasty token syntax ($name for properties, #name for colors).
149-
* Properties are only injected when the component using them is rendered.
149+
*
150+
* Tasty ships with `DEFAULT_PROPERTIES` (e.g. `$gap`, `$radius`, `#white`,
151+
* `#black`, `#clear`, `#border`, etc.) that are always included.
152+
* Properties you specify here are merged on top, so you can override any
153+
* default by using the same key.
150154
*
151155
* For color tokens (#name), `syntax: '<color>'` is auto-set and
152156
* `initialValue` defaults to `'transparent'` if not specified.
@@ -158,6 +162,8 @@ export interface TastyConfig {
158162
* '$rotation': { syntax: '<angle>', initialValue: '0deg' },
159163
* '$scale': { syntax: '<number>', inherits: false, initialValue: 1 },
160164
* '#accent': { initialValue: 'purple' }, // syntax: '<color>' auto-set
165+
* // Override a default property:
166+
* '$gap': { syntax: '<length>', inherits: true, initialValue: '8px' },
161167
* },
162168
* });
163169
*
@@ -363,14 +369,14 @@ let globalRecipes: Record<string, RecipeStyles> | null = null;
363369
let globalConfigTokens: ConfigTokens | null = null;
364370

365371
/**
366-
* Internal properties required by tasty core features.
367-
* These are always injected when styles are first generated.
372+
* Default properties shipped with tasty.
373+
* These are always included unless explicitly overridden via `configure({ properties })`.
368374
* Keys use tasty token syntax (#name for colors, $name for other properties).
369375
*
370376
* For properties with CSS @property-compatible types (length, time, number, color),
371377
* an `initialValue` is provided so the property works even without a project-level token.
372378
*/
373-
export const INTERNAL_PROPERTIES: Record<string, PropertyDefinition> = {
379+
export const DEFAULT_PROPERTIES: Record<string, PropertyDefinition> = {
374380
// Used by dual-fill feature to enable CSS transitions on the second fill color
375381
'#tasty-second-fill': {
376382
inherits: false,
@@ -390,6 +396,16 @@ export const INTERNAL_PROPERTIES: Record<string, PropertyDefinition> = {
390396
inherits: true,
391397
initialValue: 'rgb(0 0 0)',
392398
},
399+
// Shorthand for transparent
400+
'#clear': {
401+
inherits: true,
402+
initialValue: 'transparent',
403+
},
404+
// Default border color
405+
'#border': {
406+
inherits: true,
407+
initialValue: 'rgb(0 0 0)',
408+
},
393409

394410
// ---- Core design tokens used by style handlers ----
395411
// These provide sensible defaults so tasty works standalone without a design system.
@@ -530,19 +546,11 @@ export function markStylesGenerated(): void {
530546

531547
const injector = getGlobalInjector();
532548

533-
// Inject internal properties required by tasty core features
534-
for (const [token, definition] of Object.entries(INTERNAL_PROPERTIES)) {
549+
// Inject all properties (defaults merged with user-configured overrides)
550+
for (const [token, definition] of Object.entries(getEffectiveProperties())) {
535551
injector.property(token, definition);
536552
}
537553

538-
// Inject global properties if any were configured
539-
// Properties are permanent and only need to be injected once
540-
if (globalProperties && Object.keys(globalProperties).length > 0) {
541-
for (const [token, definition] of Object.entries(globalProperties)) {
542-
injector.property(token, definition);
543-
}
544-
}
545-
546554
// Inject global @font-face rules (eagerly — fonts should be available before render)
547555
if (globalFontFace && Object.keys(globalFontFace).length > 0) {
548556
for (const [family, input] of Object.entries(globalFontFace)) {
@@ -667,6 +675,15 @@ function setGlobalProperties(
667675
globalProperties = properties;
668676
}
669677

678+
/**
679+
* Get the effective properties: DEFAULT_PROPERTIES merged with user-configured
680+
* properties. User properties override defaults with matching keys.
681+
*/
682+
export function getEffectiveProperties(): Record<string, PropertyDefinition> {
683+
if (!globalProperties) return DEFAULT_PROPERTIES;
684+
return { ...DEFAULT_PROPERTIES, ...globalProperties };
685+
}
686+
670687
// ============================================================================
671688
// Global Font Face Management
672689
// ============================================================================

src/ssr/collector.ts

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,10 @@
1111
*/
1212

1313
import {
14+
getEffectiveProperties,
1415
getGlobalCounterStyle,
1516
getGlobalFontFace,
16-
getGlobalProperties,
1717
getGlobalConfigTokens,
18-
hasGlobalProperties,
19-
INTERNAL_PROPERTIES,
2018
} from '../config';
2119
import { formatCounterStyleRule } from '../counter-style';
2220
import { fontFaceContentHash, formatFontFaceRule } from '../font-face';
@@ -70,22 +68,12 @@ export class ServerStyleCollector {
7068
if (this.internalsCollected) return;
7169
this.internalsCollected = true;
7270

73-
for (const [token, definition] of Object.entries(INTERNAL_PROPERTIES)) {
71+
for (const [token, definition] of Object.entries(
72+
getEffectiveProperties(),
73+
)) {
7474
const css = formatPropertyCSS(token, definition);
7575
if (css) {
76-
this.collectProperty(`__internal:${token}`, css);
77-
}
78-
}
79-
80-
if (hasGlobalProperties()) {
81-
const globalProps = getGlobalProperties();
82-
if (globalProps) {
83-
for (const [token, definition] of Object.entries(globalProps)) {
84-
const css = formatPropertyCSS(token, definition);
85-
if (css) {
86-
this.collectProperty(`__global:${token}`, css);
87-
}
88-
}
76+
this.collectProperty(`__prop:${token}`, css);
8977
}
9078
}
9179

src/ssr/ssr.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ describe('ServerStyleCollector', () => {
288288
collector.collectInternals();
289289

290290
const css = collector.getCSS();
291-
// INTERNAL_PROPERTIES: gap, radius, border-width, font fallbacks, etc.
291+
// DEFAULT_PROPERTIES: gap, radius, border-width, font fallbacks, etc.
292292
expect(css).toContain('@property --gap');
293293
expect(css).toContain('@property --radius');
294294
expect(css).toContain('@property --border-width');

0 commit comments

Comments
 (0)