Skip to content

Commit 50145b3

Browse files
committed
Feat: theme - Support raw method #268
1 parent 743384f commit 50145b3

File tree

2 files changed

+100
-13
lines changed

2 files changed

+100
-13
lines changed

.changeset/dull-bugs-send.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@mincho-js/css": minor
3+
---
4+
5+
6+
**theme**
7+
- Add `theme()` `this.raw()` API

packages/css/src/theme/index.ts

Lines changed: 93 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ type WithOptionalLayer<T extends Theme> = T & {
3535

3636
interface ThemeSubFunctions {
3737
fallbackVar: typeof fallbackVar;
38+
raw(varReference: unknown): string;
3839
}
3940

4041
type ResolveThemeOutput<T extends Theme> = Resolve<ResolveTheme<T>>;
@@ -132,6 +133,14 @@ function assignTokensWithPrefix<ThemeTokens extends Theme>(
132133
writable: false
133134
});
134135

136+
// Add raw utility to extract actual values instead of var() references
137+
Object.defineProperty(resolvedTokens, "raw", {
138+
value: createRawExtractor(vars),
139+
enumerable: false,
140+
configurable: false,
141+
writable: false
142+
});
143+
135144
// Execute two-pass token resolution:
136145
// 1. Assign CSS variables to all tokens
137146
// 2. Resolve semantic token references
@@ -475,6 +484,25 @@ function setByPath(
475484
}
476485
}
477486

487+
/**
488+
* Creates a function that extracts raw CSS values from var() references.
489+
* Used to "hardcode" values instead of using CSS variable references.
490+
*/
491+
function createRawExtractor(vars: AssignedVars) {
492+
return function raw(varReference: unknown) {
493+
if (typeof varReference === "string") {
494+
// Use getVarName utility to extract the CSS variable name
495+
const varName = getVarName(varReference);
496+
// If it was a var() reference and we have the value, return it
497+
if (varName !== varReference && varName in vars) {
498+
return vars[varName];
499+
}
500+
}
501+
// If not a var reference or lookup failed, return as-is
502+
return varReference;
503+
};
504+
}
505+
478506
// == Token Value Extractors ==================================================
479507
function extractFontFamilyValue(value: TokenFontFamilyValue): CSSVarValue {
480508
if (Array.isArray(value)) {
@@ -579,7 +607,7 @@ function extractTokenDefinitionValue(definition: TokenDefinition): CSSVarValue {
579607

580608
function extractCSSValue(value: TokenValue): CSSVarValue {
581609
if (isPrimitive(value)) {
582-
return String(value) as CSSVarValue;
610+
return value as CSSVarValue;
583611
}
584612

585613
if (isTokenUnitValue(value)) {
@@ -687,9 +715,9 @@ if (import.meta.vitest) {
687715
// Compare normalized values (without hashes)
688716
expect(normalizeVars(result.vars)).toEqual({
689717
"--color": "red",
690-
"--size": "16",
691-
"--enabled": "true",
692-
"--nothing": "undefined"
718+
"--size": 16,
719+
"--enabled": true,
720+
"--nothing": undefined
693721
});
694722

695723
expect(normalizeResolvedTokens(result.resolvedTokens)).toEqual({
@@ -712,7 +740,7 @@ if (import.meta.vitest) {
712740
expect(normalizeVars(result.vars)).toEqual({
713741
"--background-color": "white",
714742
"--font-size": "16px",
715-
"--line-height": "1.5"
743+
"--line-height": 1.5
716744
});
717745

718746
expect(normalizeResolvedTokens(result.resolvedTokens)).toEqual({
@@ -730,11 +758,11 @@ if (import.meta.vitest) {
730758
validateHashFormat(result.vars);
731759

732760
expect(normalizeVars(result.vars)).toEqual({
733-
"--space-0": "2",
734-
"--space-1": "4",
735-
"--space-2": "8",
736-
"--space-3": "16",
737-
"--space-4": "32"
761+
"--space-0": 2,
762+
"--space-1": 4,
763+
"--space-2": 8,
764+
"--space-3": 16,
765+
"--space-4": 32
738766
});
739767

740768
expect(normalizeResolvedTokens(result.resolvedTokens)).toEqual({
@@ -866,6 +894,58 @@ if (import.meta.vitest) {
866894
);
867895
});
868896

897+
it("handles raw value extraction in semantic tokens", () => {
898+
const result = assignTokens(
899+
composedValue({
900+
color: {
901+
base: {
902+
blue: "#0000ff",
903+
red: "#ff0000"
904+
},
905+
semantic: {
906+
get primary(): string {
907+
// Use raw to get the actual value instead of var() reference
908+
return this.raw(this.color.base.blue);
909+
},
910+
get danger(): string {
911+
// Test raw with another token
912+
return this.raw(this.color.base.red);
913+
},
914+
get custom(): string {
915+
// Test raw with a non-var value
916+
return this.raw("#00ff00");
917+
}
918+
}
919+
},
920+
space: {
921+
base: [2, 4, 8, 16, 32, 64],
922+
semantic: {
923+
get small(): string {
924+
return this.raw(this.space.base[1]);
925+
},
926+
get medium(): string {
927+
return this.raw(this.space.base[3]);
928+
},
929+
get large(): string {
930+
return this.raw(this.space.base[5]);
931+
}
932+
}
933+
}
934+
})
935+
);
936+
937+
validateHashFormat(result.vars);
938+
939+
const normalizedVars = normalizeVars(result.vars);
940+
// The semantic tokens should contain the raw values, not var() references
941+
expect(normalizedVars["--color-semantic-primary"]).toBe("#0000ff");
942+
expect(normalizedVars["--color-semantic-danger"]).toBe("#ff0000");
943+
expect(normalizedVars["--color-semantic-custom"]).toBe("#00ff00");
944+
expect(normalizedVars["--space-semantic-small"]).toBe(4);
945+
expect(normalizedVars["--space-semantic-medium"]).toBe(16);
946+
expect(normalizedVars["--space-semantic-large"]).toBe(64);
947+
});
948+
869949
it("handles TokenUnitValue", () => {
870950
const result = assignTokens({
871951
spacing: { value: 1.5, unit: "rem" },
@@ -1170,14 +1250,14 @@ if (import.meta.vitest) {
11701250
validateHashFormat(result.vars);
11711251
const normalized = normalizeVars(result.vars);
11721252

1173-
expect(normalized["--typography-heading-sizes-0"]).toBe("48");
1174-
expect(normalized["--typography-heading-sizes-4"]).toBe("16");
1253+
expect(normalized["--typography-heading-sizes-0"]).toBe(48);
1254+
expect(normalized["--typography-heading-sizes-4"]).toBe(16);
11751255
expect(normalized["--typography-heading-weight"]).toBe("700");
11761256
expect(normalized["--typography-heading-family"]).toBe(
11771257
"Helvetica, sans-serif"
11781258
);
11791259
expect(normalized["--typography-body-size"]).toBe("14px");
1180-
expect(normalized["--typography-body-line-height"]).toBe("1.5");
1260+
expect(normalized["--typography-body-line-height"]).toBe(1.5);
11811261
expect(normalized["--colors-primary"]).toBe("#007bff");
11821262
expect(normalized["--colors-secondary"]).toBe("#6c757d");
11831263
});

0 commit comments

Comments
 (0)