Skip to content

Commit

Permalink
feat(theme): drastically improve theme hydration; fix variants hydration
Browse files Browse the repository at this point in the history
  • Loading branch information
Tahul committed Dec 23, 2022
1 parent 45ce6e4 commit c30d403
Show file tree
Hide file tree
Showing 9 changed files with 105 additions and 34 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,6 @@ dist
# Nuxt
.nuxt
.output

# vite-plugin-inspect
.vite-inspect
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@
"@antfu/eslint-config": "^0.34.0",
"@nuxt/kit": "^3.0.0",
"@nuxt/test-utils": "^3.0.0",
"@nuxthq/studio": "^0.4.0",
"@nuxthq/studio": "^0.4.1",
"@nuxtjs/color-mode": "^3.2.0",
"@types/chroma-js": "^2.1.4",
"@types/node": "^18.11.17",
Expand Down
4 changes: 3 additions & 1 deletion playground/_vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ export default defineConfig({
},
},
plugins: [
Inspect(),
Inspect({
dev: true,
}),
Pinceau({
configFileName: 'tokens.config',
configOrPaths: [
Expand Down
8 changes: 7 additions & 1 deletion playground/app.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
<template>
<NuxtLayout>
<NuxtPage />
<Container>
<NuxtPage />
</Container>

<RouterLink to="/content">
Test
</RouterLink>
</NuxtLayout>
</template>

Expand Down
56 changes: 56 additions & 0 deletions playground/components/Container.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<script setup lang="ts">
import type { PropType } from 'vue'
type HTMLElementsTags = keyof HTMLElementTagNameMap
defineProps({
as: {
type: String as PropType<HTMLElementsTags>,
required: false,
default: 'div',
},
...variants,
})
</script>

<template>
<component :is="as" class="container">
<slot />
</component>
</template>

<style lang="ts" scoped>
css({
'.container': {
marginLeft: 'auto',
marginRight: 'auto',
width: '100%',
height: '100%'
},
variants: {
padded: {
true: {
px: '{space.32}',
'@sm': {
px: '{space.48}',
},
'@md': {
px: '{space.64}',
}
},
options: {
default: true
}
},
fluid: {
true: {},
false: {
maxWidth: '100vw'
},
options: {
default: false
}
}
}
})
</style>
9 changes: 5 additions & 4 deletions playground/layouts/default.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import ThemeSelect from '../components/ThemeSelect.vue'
class="layout"
>
<header>
<span>
🖌&nbsp;Pinceau playground
</span>
<RouterLink to="/">
<span>
🖌&nbsp;Pinceau playground
</span>
</RouterLink>

<div>
<ThemeSelect />
Expand Down Expand Up @@ -93,7 +95,6 @@ html, body, #app, #__nuxt {
flex: 1;
height: 100%;
width: 100%;
padding: 2rem;
z-index: 50;
}
}
Expand Down
2 changes: 2 additions & 0 deletions playground/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Block from './components/Block.vue'
import BigButton from './components/BigButton.vue'
import Alert from './components/Alert.vue'
import ClientOnly from './theme/ClientOnly.vue'
import Container from './components/Container.vue'
import PlaygroundGrid from './components/PlaygroundGrid.vue'
import NuxtLayout from './layouts/default.vue'
import Index from './pages/index.vue'
Expand Down Expand Up @@ -38,6 +39,7 @@ app.component('Block', Block)
app.component('BigButton', BigButton)
app.component('Alert', Alert)
app.component('ClientOnly', ClientOnly)
app.component('Container', Container)
app.component('PlaygroundGrid', PlaygroundGrid)
app.component('NuxtLayout', NuxtLayout)
app.component('NuxtPage', RouterView)
Expand Down
11 changes: 5 additions & 6 deletions playground/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
import JSConfetti from 'js-confetti'
import { onMounted, ref } from 'vue'
const colors = ['pink', 'primary', 'red', 'blue']
const rand = (items: any) => items[Math.floor(Math.random() * items.length)]
let confettiInstance: JSConfetti
const canvas = ref()
onMounted(
Expand All @@ -25,7 +29,7 @@ const confettis = () => {
<section>
<canvas ref="canvas" />
<PlaygroundGrid>
<BigButton :padded="{ initial: 'sm', lg: 'lg', xl: 'xl' }" color="pink" @click="confettis()" />
<BigButton :padded="{ initial: 'sm', lg: 'lg', xl: 'xl' }" :color="rand(colors)" @click="confettis()" />
</PlaygroundGrid>
</section>
</template>
Expand All @@ -40,11 +44,6 @@ css({
section: {
overflow: 'hidden',
position: 'relative',
'& > div': {
display: 'flex',
flexDirection: 'column',
gap: '1rem',
}
},
canvas: {
position: 'absolute',
Expand Down
44 changes: 23 additions & 21 deletions src/runtime/features/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ export function usePinceauThemeSheet(
// Resolved theme object from the stylesheet.
const theme = ref<any>(initialTheme || {})

// Local cache for each token CSSRule index.
const cache = {}
// Local cache for each mq CSSRules.
const cache: { [key: string]: any } = {}

// Resolve stylesheet on boot
resolveStylesheet()
Expand Down Expand Up @@ -77,24 +77,31 @@ export function usePinceauThemeSheet(
// Hydrate cache with resolved values from sheet
Object
.entries(cssRules || {})
.filter(([_, rule]: any) => {
if (rule?.selectorText === '.pinceau-theme[--]') { return false }
if (rule?.type !== 4 && !rule?.cssText?.includes('--pinceau-mq')) { return false }
return true
})
.forEach(
([_, rule]: any) => {
// Filter
if (rule?.type !== 4 && !rule?.cssText?.includes('--pinceau-mq')) { return false }
if (rule?.selectorText === '.pinceau-theme[--]') { return false }

// Get current theme from parsed cssRules
let currentTheme = 'root'

rule
.cssText
// Regex-based hydration
rule.cssText
.match(/--([\w-]+)\s*:\s*(.+?);/gm)
.map((matchedRule) => {
const rule = matchedRule.replace(';', '').split(': ')
if (rule[0] === '--pinceau-mq') { currentTheme = rule[1] }
return rule
.map((match) => {
const [variable, value] = match.replace(';', '').split(/:\s(.*)/s)
if (variable === '--pinceau-mq') {
currentTheme = value
// Assign cache rule references
if (!cache[value]) {
const ruleReference = (Object.entries(rule?.cssRules || {}).find(([_, cssRule]: any) => cssRule?.cssText.includes(`--pinceau-mq: ${value}`)))?.[1]
if (ruleReference) { cache[value] = ruleReference as CSSStyleRule }
}
}
return [variable, value]
})
.forEach(([key, value]: any) => setThemeValue(key, value, currentTheme))
.forEach(([variable, value]: any) => setThemeValue(variable, value, currentTheme))
},
)
}
Expand Down Expand Up @@ -131,15 +138,10 @@ export function usePinceauThemeSheet(
* Update a specific token from its variable and a value.
*/
function updateVariable(variable, value) {
// Find cached rule reference corresponding to the CSS variable
const cachedValue = cache[`--${variable}`]
// No cached value found
// TODO: Create new value if not found?
if (!cachedValue) { return }
// Handle `mq` object passed as value
if (typeof value === 'object') { Object.entries(value).forEach(([mq, mqValue]) => cachedValue?.[mq]?.setProperty(`--${pathToVarName(variable)}`, mqValue)) }
if (typeof value === 'object') { Object.entries(value).forEach(([mq, mqValue]) => cache?.[mq]?.style.setProperty(`--${pathToVarName(variable)}`, mqValue)) }
// Handle flat value
else { cachedValue?.root?.setProperty(`--${pathToVarName(variable)}`, value) }
else { cache?.root?.style.setProperty(`--${pathToVarName(variable)}`, value) }
}

/**
Expand Down

0 comments on commit c30d403

Please sign in to comment.