-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added a custom preset + transformers
- Loading branch information
1 parent
58e3b7b
commit 65fd00b
Showing
8 changed files
with
360 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import type { Preset } from 'unocss' | ||
import type { PresetYouCanOptions } from './types' | ||
|
||
declare function presetYouCan(options?: PresetYouCanOptions): Preset | ||
|
||
export default presetYouCan |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import animations from './src/animation.tokens.json'; | ||
import colors from './src/colors.tokens.json'; | ||
import radius from './src/radius.tokens.json'; | ||
import spacing from './src/spacing.tokens.json'; | ||
import typography from './src/typography.tokens.json'; | ||
|
||
import { createColorScale, flattenObject } from './utils'; | ||
|
||
import { | ||
transformAnimations, | ||
transformColors, | ||
transformRadius, | ||
// transformShadows, | ||
transformSpacing, | ||
transformTypography, | ||
} from './transformers'; | ||
|
||
const presetYouCan = () => { | ||
const flattenedColors = flattenObject(colors); | ||
const semanticColors = createColorScale(colors); | ||
const flattenedAnimations = flattenObject(animations); | ||
const flattenedRadius = flattenObject(radius); | ||
// const flattenedShadows = flattenObject(shadows); | ||
const flattenedSpacing = flattenObject(spacing); | ||
const flattenedTypography = flattenObject(typography); | ||
|
||
const colorRules = [ | ||
[/^text-(.+)-(\d+)$/, ([, color, shade]: string[]) => { | ||
if (semanticColors[color]?.[shade]) { | ||
return { | ||
color: semanticColors[color][shade], | ||
}; | ||
} | ||
}], | ||
[/^bg-(.+)-(\d+)$/, ([, color, shade]: string[]) => { | ||
if (semanticColors[color]?.[shade]) { | ||
return { | ||
'background-color': semanticColors[color][shade], | ||
}; | ||
} | ||
}], | ||
[/^border-(.+)-(\d+)$/, ([, color, shade]: string[]) => { | ||
if (semanticColors[color]?.[shade]) { | ||
return { | ||
'border-color': semanticColors[color][shade], | ||
}; | ||
} | ||
}], | ||
]; | ||
|
||
return { | ||
name: '@youcan/tokens', | ||
theme: { | ||
colors: transformColors(flattenedColors), | ||
transitionTimingFunction: transformAnimations(flattenedAnimations), | ||
borderRadius: transformRadius(flattenedRadius), | ||
// boxShadow: transformShadows(flattenedShadows), | ||
spacing: transformSpacing(flattenedSpacing), | ||
...transformTypography(flattenedTypography), | ||
}, | ||
rules: colorRules, | ||
variants: [ | ||
// Hover variant | ||
(matcher: string) => { | ||
if (!matcher.startsWith('hover:')) { | ||
return matcher; | ||
} | ||
|
||
return { | ||
matcher: matcher.slice(6), | ||
selector: (s: string) => `${s}:hover`, | ||
}; | ||
}, | ||
// Focus variant | ||
(matcher: string) => { | ||
if (!matcher.startsWith('focus:')) { | ||
return matcher; | ||
} | ||
|
||
return { | ||
matcher: matcher.slice(6), | ||
selector: (s: string) => `${s}:focus`, | ||
}; | ||
}, | ||
// Active variant | ||
(matcher: string) => { | ||
if (!matcher.startsWith('active:')) { | ||
return matcher; | ||
} | ||
|
||
return { | ||
matcher: matcher.slice(7), | ||
selector: (s: string) => `${s}:active`, | ||
}; | ||
}, | ||
], | ||
}; | ||
}; | ||
|
||
export default presetYouCan; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
{ | ||
"name": "@youcan/tokens", | ||
"type": "module", | ||
"version": "1.0.0", | ||
"main": "dist/index.js", | ||
"module": "./dist/index.js", | ||
"types": "./dist/index.d.ts", | ||
"files": [ | ||
"dist" | ||
], | ||
"scripts": { | ||
"build": "tsup", | ||
"dev": "tsup --watch" | ||
}, | ||
"peerDependencies": { | ||
"unocss": "^0.58.0" | ||
}, | ||
"devDependencies": { | ||
"tsup": "^8.0.0", | ||
"typescript": "^5.0.0", | ||
"unocss": "^0.58.0" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import type { AnimationToken, RadiusToken, SpacingToken, TypographyToken } from './types'; | ||
import { createColorScale } from './utils'; | ||
|
||
export const transformAnimations = (tokens: Record<string, AnimationToken>) => { | ||
const transitions: Record<string, string> = {}; | ||
|
||
Object.entries(tokens).forEach(([key, token]) => { | ||
transitions[key] = `${token.$value} ${token.$extension?.easing || 'ease-out'}`; | ||
}); | ||
|
||
return transitions; | ||
}; | ||
|
||
export const transformColors = (tokens: Record<string, any>) => { | ||
const semanticColors = createColorScale(tokens); | ||
const flatColors: Record<string, string> = {}; | ||
|
||
Object.entries(semanticColors).forEach(([category, shades]) => { | ||
Object.entries(shades).forEach(([shade, value]) => { | ||
flatColors[`${category}-${shade}`] = value; | ||
}); | ||
}); | ||
|
||
return flatColors; | ||
}; | ||
|
||
export const transformRadius = (tokens: Record<string, RadiusToken>) => { | ||
const radius: Record<string, string> = {}; | ||
|
||
Object.entries(tokens).forEach(([key, token]) => { | ||
radius[key] = `${token.$value}px`; | ||
}); | ||
|
||
return radius; | ||
}; | ||
|
||
// export const transformShadows = (tokens: Record<string, ShadowToken>) => { | ||
// const shadows: Record<string, string> = {}; | ||
|
||
// Object.entries(tokens).forEach(([key, token]) => { | ||
// const shadowValues = Array.isArray(token.$value) ? token.$value : [token.$value]; | ||
// shadows[key] = shadowValues | ||
// .map(shadow => | ||
// `${shadow.x}px ${shadow.y}px ${shadow.blur}px ${shadow.spread}px ${shadow.color}`, | ||
// ) | ||
// .join(', '); | ||
// }); | ||
|
||
// return shadows; | ||
// }; | ||
|
||
export const transformSpacing = (tokens: Record<string, SpacingToken>) => { | ||
const spacing: Record<string, string> = {}; | ||
|
||
Object.entries(tokens).forEach(([key, token]) => { | ||
spacing[key] = `${token.$value}px`; | ||
}); | ||
|
||
return spacing; | ||
}; | ||
|
||
export const transformTypography = (tokens: Record<string, TypographyToken>) => { | ||
const typography: Record<string, any> = { | ||
fontFamily: {}, | ||
lineHeight: {}, | ||
fontWeight: {}, | ||
fontSize: {}, | ||
}; | ||
|
||
Object.entries(tokens).forEach(([key, token]) => { | ||
switch (token.$type) { | ||
case 'fontFamilies': | ||
typography.fontFamily[key] = token.$value; | ||
break; | ||
case 'lineHeights': | ||
typography.lineHeight[key] = `${token.$value}px`; | ||
break; | ||
case 'fontWeights': | ||
typography.fontWeight[key] = token.$value; | ||
break; | ||
case 'fontSize': | ||
typography.fontSize[key] = `${token.$value}px`; | ||
break; | ||
} | ||
}); | ||
|
||
return typography; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
{ | ||
"compilerOptions": { | ||
"target": "ES2020", | ||
"useDefineForClassFields": true, | ||
"module": "ESNext", | ||
"lib": ["ES2020", "DOM", "DOM.Iterable"], | ||
"skipLibCheck": true, | ||
"moduleResolution": "bundler", | ||
"allowImportingTsExtensions": true, | ||
"resolveJsonModule": true, | ||
"isolatedModules": true, | ||
"noEmit": true, | ||
"strict": true, | ||
"noUnusedLocals": true, | ||
"noUnusedParameters": true, | ||
"noFallthroughCasesInSwitch": true, | ||
"outDir": "./dist", | ||
"declaration": true | ||
}, | ||
"include": ["./"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { defineConfig } from 'tsup'; | ||
|
||
export default defineConfig({ | ||
entry: ['./index.ts'], | ||
format: ['esm', 'cjs'], | ||
dts: true, | ||
clean: true, | ||
external: ['unocss'], | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
export interface DesignToken { | ||
$value: any | ||
$type: string | ||
$extension?: { | ||
easing?: string | ||
} | ||
} | ||
|
||
export interface TokenObject { | ||
[key: string]: TokenObject | DesignToken | ||
} | ||
|
||
export interface AnimationToken extends DesignToken { | ||
$value: string | ||
$type: 'duration' | ||
$extension: { | ||
easing: string | ||
} | ||
} | ||
|
||
export interface ColorToken extends DesignToken { | ||
$value: string | ||
$type: 'color' | ||
} | ||
|
||
export interface RadiusToken extends DesignToken { | ||
$value: number | ||
$type: 'number' | ||
} | ||
|
||
export interface ShadowValue { | ||
color: string | ||
$type: 'dropShadow' | ||
x: number | ||
y: number | ||
blur: number | ||
spread: number | ||
} | ||
|
||
export interface ShadowToken extends DesignToken { | ||
$value: ShadowValue | ShadowValue[] | ||
$type: 'boxShadow' | ||
} | ||
|
||
export interface SpacingToken extends DesignToken { | ||
$value: number | ||
$type: 'number' | ||
} | ||
|
||
export interface TypographyToken extends DesignToken { | ||
$value: string | number | ||
$type: 'fontFamilies' | 'lineHeights' | 'fontWeights' | 'fontSize' | ||
} | ||
|
||
// interface SemanticColorToken extends DesignToken { | ||
// $value: string | ||
// $type: 'color' | ||
// } | ||
|
||
// interface SemanticColors { | ||
// [key: string]: { | ||
// [shade: string]: SemanticColorToken | ||
// } | ||
// } | ||
|
||
export interface PresetYouCanOptions { | ||
prefix?: string | ||
colorPrefix?: string | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import type { TokenObject } from './types'; | ||
|
||
export const flattenObject = (obj: TokenObject, prefix = ''): Record<string, any> => { | ||
return Object.entries(obj).reduce((acc, [key, value]) => { | ||
const newPrefix = prefix ? `${prefix}-${key}` : key; | ||
|
||
if (value && typeof value === 'object' && !('$value' in value)) { | ||
return { ...acc, ...flattenObject(value, newPrefix) }; | ||
} | ||
|
||
if ('$value' in value) { | ||
return { ...acc, [newPrefix]: value.$value }; | ||
} | ||
|
||
return acc; | ||
}, {}); | ||
}; | ||
|
||
export const resolveTokenReference = (value: string, tokens: Record<string, any>): string => { | ||
if (typeof value !== 'string' || !value.startsWith('{') || !value.endsWith('}')) { | ||
return value; | ||
} | ||
|
||
const tokenPath = value.slice(1, -1); | ||
|
||
return tokens[tokenPath] || value; | ||
}; | ||
|
||
export const createColorScale = (colors: Record<string, any>) => { | ||
const semanticColors: Record<string, Record<string, string>> = {}; | ||
|
||
Object.entries(colors).forEach(([key, value]) => { | ||
if (typeof value === 'object' && !('$type' in value)) { | ||
semanticColors[key] = {}; | ||
Object.entries(value).forEach(([shade, colorValue]) => { | ||
if (typeof colorValue === 'object' && '$value' in colorValue!) { | ||
semanticColors[key][shade] = colorValue.$value as string; | ||
} | ||
}); | ||
} | ||
}); | ||
|
||
return semanticColors; | ||
}; |