-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
42 changed files
with
1,987 additions
and
2,541 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 |
---|---|---|
|
@@ -16,3 +16,7 @@ coverage | |
|
||
# Temp files | ||
.tmp | ||
|
||
## Panda | ||
styled-system | ||
styled-system-studio |
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
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,103 @@ | ||
import postcss from 'postcss' | ||
|
||
/** | ||
* Based on https://panda-css.com/docs/concepts/hooks#remove-unused-variables-from-final-css | ||
*/ | ||
|
||
interface UseRecord { | ||
uses: number | ||
dependencies: Set<string> | ||
declarations: Set<postcss.Declaration> | ||
} | ||
|
||
const varRegex = /var\(\s*(?<name>--[^ ,);]+)/g | ||
|
||
export default function removeUnusedCSS(css: string): string { | ||
const root = postcss.parse(css) | ||
|
||
const records = new Map<string, UseRecord>() | ||
const keyframes = new Map<string, boolean>() | ||
|
||
const getRecord = (variable: string): UseRecord => { | ||
let record = records.get(variable) | ||
if (record == null) { | ||
record = { uses: 0, dependencies: new Set(), declarations: new Set() } | ||
records.set(variable, record) | ||
} | ||
return record | ||
} | ||
|
||
const registerUse = (variable: string, ignoreList = new Set<string>()): void => { | ||
const record = getRecord(variable) | ||
record.uses++ | ||
ignoreList.add(variable) | ||
for (const dependency of record.dependencies) { | ||
if (!ignoreList.has(dependency)) registerUse(dependency, ignoreList) | ||
} | ||
} | ||
|
||
const registerDependency = (variable: string, dependency: string): void => { | ||
const record = getRecord(variable) | ||
record.dependencies.add(dependency) | ||
} | ||
|
||
// Detect variable uses | ||
root.walkDecls((decl) => { | ||
const parent = decl.parent | ||
if (parent == null) return | ||
|
||
if (parent.type === 'rule' && (parent as postcss.Rule).selector === ':root') { | ||
return | ||
} | ||
|
||
const isVar = decl.prop.startsWith('--') | ||
|
||
// Initiate record | ||
if (isVar) getRecord(decl.prop).declarations.add(decl) | ||
|
||
if (!decl.value.includes('var(')) return | ||
|
||
for (const match of decl.value.matchAll(varRegex)) { | ||
const variable = match.groups?.name.trim() | ||
if (variable == null || variable === '') continue | ||
|
||
if (isVar) { | ||
registerDependency(decl.prop, variable) | ||
} else { | ||
registerUse(variable) | ||
} | ||
} | ||
}) | ||
|
||
root.walk((node) => { | ||
if (node.type === 'atrule' && node.name === 'keyframes') { | ||
// Record the keyframe and mark it as unused | ||
keyframes.set(node.params, false) | ||
} else if (node.type === 'decl') { | ||
const decl = node | ||
const animationName = decl.prop === 'animation' ? decl.value.split(' ')[0] : decl.value | ||
|
||
if ((decl.prop === 'animation' || decl.prop === 'animation-name') && keyframes.has(animationName)) { | ||
// Mark the keyframe as used | ||
keyframes.set(animationName, true) | ||
} | ||
} | ||
}) | ||
|
||
// Remove unused variables | ||
for (const { uses, declarations } of records.values()) { | ||
if (uses > 0) continue | ||
|
||
for (const decl of declarations) { | ||
const node = decl.parent?.nodes.length === 1 ? decl.parent : decl | ||
node.remove() | ||
} | ||
} | ||
|
||
// Remove unused keyframes | ||
root.walkAtRules('keyframes', (rule) => { | ||
if (keyframes.get(rule.params) === false) rule.remove() | ||
}) | ||
|
||
return root.toString() | ||
} |
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
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,65 @@ | ||
import { defineConfig, defineGlobalStyles } from '@pandacss/dev' | ||
import removeUnusedCSS from './lib/panda/remove-unused-css.js' | ||
import * as theme from './src/theme/index.js' | ||
|
||
export default defineConfig({ | ||
preflight: false, | ||
importMap: 'styled-system', | ||
include: ['./src/**/*.{js,jsx,ts,tsx}'], | ||
exclude: [], | ||
conditions: { | ||
extend: { | ||
dark: '@media (prefers-color-scheme: dark)', | ||
typeDate: '&[type="date"]', | ||
}, | ||
}, | ||
theme: { | ||
extend: { | ||
tokens: { | ||
fonts: { | ||
Digital7Mono: { value: theme.fonts.Digital7Mono }, | ||
}, | ||
colors: buildPallete(theme.colors), | ||
}, | ||
}, | ||
}, | ||
globalCss: defineGlobalStyles({ | ||
html: { | ||
'-webkit-text-size-adjust': '100%', | ||
'-webkit-tap-highlight-color': 'rgba(0, 0, 0, 0)', | ||
'-webkit-font-smoothing': 'antialiased', | ||
fontFamily: | ||
"'Helvetica Neue', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'", | ||
fontWeight: 400, | ||
fontSize: '10px', | ||
boxSizing: 'border-box', | ||
background: { base: 'white', _dark: 'dark1' }, | ||
}, | ||
body: { | ||
margin: 0, | ||
touchAction: 'manipulation', | ||
fontFamily: 'inherit', | ||
fontSize: 'inherit', | ||
}, | ||
'*, *::before, *::after': { | ||
boxSizing: 'inherit', | ||
}, | ||
'html, body, #app': { | ||
height: 'full', | ||
width: 'full', | ||
}, | ||
}), | ||
outdir: 'styled-system', | ||
hooks: { | ||
'cssgen:done': ({ artifact, content }) => { | ||
if (artifact === 'styles.css') return removeUnusedCSS(content) | ||
}, | ||
}, | ||
}) | ||
|
||
function buildPallete<T extends Record<string, string>>(colors: T): Record<keyof T, { value: string }> { | ||
return Object.fromEntries(Object.entries(colors).map(([key, value]) => [key, { value }])) as Record< | ||
keyof T, | ||
{ value: string } | ||
> | ||
} |
Oops, something went wrong.