Pinceau Documentation

repository·main·Indexed 20 days ago

https://github.com/tahul/pinceau

A typed styling engine and design system tool for Vite-based frameworks including Vue, React, and Svelte. Pinceau provides a robust API for managing design tokens, responsive variants, and theme swapping with SSR optimization. It features a Vite plugin and Nuxt module that allow developers to move styling logic into a structured theme configuration using a typed styling API, a VSCode extension for IntelliSense, and a Pinceau REPL for Vue 3.

Tokens
51.6K
Snippets
182
Records
227
Agent score
71%

What's inside Pinceau

  1. Overview of Pinceau VSCode extension

    main
    Pinceau VSCode is an IntelliSense integration for Pinceau features. It is designed to improve the developer experience by making <script> blocks lighter and <style> blocks smarter within your editor. It provides autocompletion and intelligence specifically for Pinceau-related syntax and features.
  2. Overview of Pinceau features

    main

    Pinceau is a typed styling API designed to make <script> blocks lighter and <style> blocks smarter. It is built for modern design systems and provides:

    • Typed Styling API: Inspired by Stitches, allowing for component-based styling ($styled.a), scoped CSS (styled), and global CSS (css).
    • Design Token Support: Multi-layer configuration compatible with Design Tokens community standards.
    • Vite Integration: Plug & play support for Vue, React, Svelte, Nuxt, and Astro.
    • Robust Design System Tools: Includes responsive variants, style composition, style colocation, and runtime theme swapping.
    • SSR Optimized: Ready for Server-Side Rendering with optimized hydration and no JS/CSS duplication.
    • Developer Experience: Includes a VSCode extension for enhanced DX.
  3. Define component variations with Variants

    main

    The variants key within the css() function allows you to create reusable component appearances (like size or color variations).

    How to implement variants:

    1. In Script: Spread the variants object into your defineProps() call.
    2. In CSS: Add a variants key to your css() object. Each variant name (e.g., size) contains sub-objects for each option (e.g., sm, md).

    Usage modes:

    • String prop: Pass a single value: <MyComponent size="sm" />.
    • Object prop (Responsive): Pass an object mapping media queries to variant values: <MyComponent :size="{ initial: 'sm', lg: 'lg' }" />.

    Example:

    <script setup lang="ts">
    import { computedStyle } from 'pinceau/runtime'
    
    defineProps({
      color: computedStyle<keyof PinceauTheme['color']>('red'),
      ...variants,
    })
    </script>
    
    <style scoped lang="ts">
    css({
      '.my-button': {
        display: 'inline-block',
      },
      'variants': {
        size: {
          sm: { span: { padding: '{space.3} {space.6}' } },
          md: { span: { padding: '{space.6} {space.8}' } },
          options: {
            default: 'sm',
          },
        },
      },
    })
    </style>
  4. How Variants work in Pinceau

    main

    Variants allow you to declare different component appearances that respond to media queries via props. You define them within the variants key at the root of the css() function.

    When you define a variant, Pinceau automatically generates the corresponding Vue props. Every key in the variants object becomes a prop name. These props can be passed as simple values (like a string or boolean) or as responsive objects that specify values for different media queries using the initial key.

    <script setup lang="ts">
    // Spread the generated variants into defineProps
    const props = defineProps({
      ...variants
    })
    </script>
    
    <template>
      <div class="root">
        <slot />
      </div>
    </template>
    
    <style lang="ts">
    css({
      variants: {
        size: {
          sm: { padding: '{space.3} {space.6}' },
          md: { padding: '{space.6} {space.8}' },
          options: { default: 'sm' }
        },
      },
    })
    </style>
  5. Configure color scheme resolution mode

    main

    The colorSchemeMode option determines how Pinceau handles dark/light mode. You can choose between 'media' (using CSS media queries) or 'class' (using a CSS class like .dark on a root element).

    • 'media' mode: Uses @media (prefers-color-scheme: dark).
    • 'class' mode: Uses a selector like :root.dark.
    /* Example of 'class' mode output */
    .my-button {
      background-color: var(--color-gray-100);
    }
    
    :root.dark .my-button {
      background-color: var(--color-gray-900);
    }
  6. When to use the `value` object syntax for tokens

    main

    While you can define tokens as simple key/value pairs (e.g., myColor: 'red'), you can also use the explicit object syntax (e.g., myColor: { value: 'red' }). Using the value object is optional and has no impact on outputs if used alone, but it becomes useful when you need to:

    • Use other Design Token attributes for later consumption.
    • Use the $schema key for a specific token.
    • Synchronize with tokens generated by external tools like Figma Tokens.
    • Maintain a specific organizational preference.
    // Explicit value syntax
    myColor: { value: 'red' }
    
    // Simple syntax (automatically normalized to the above)
    myColor: 'red'
  7. Use Utils properties in css()

    main

    Pinceau integrates Utils properties directly into the css() function. When you define a utility in your theme, it is treated as a first-class property with full TypeScript autocomplete support.

    If a utility returns nested CSS, the compiler unwraps it using the key position as the root.

    Example Theme Definition:

    defineTheme({
      utils: {
        mx: value => ({ marginRight: value, marginLeft: value })
      }
    })

    Usage in css():

    css({
      '.my-button': {
        // 'mx' will be suggested by autocomplete
        mx: 2
      }
    })
  8. Define responsive tokens in your theme configuration

    main

    Responsive tokens allow you to define how a token's value changes at specific breakpoints or color schemes directly within the theme configuration, rather than handling responsiveness inside individual component styles.

    A token is recognized as responsive when its value is an object containing an initial key (the default value used without any media queries) and other keys corresponding to your media queries or color schemes.

    Supported keys for responsive tokens include:

    • Any key defined in the media section of your theme.config.
    • The native dark and light keys for color scheme switching.
    • Token references (e.g., using $color.blue.9).

    This pattern also applies to Variants and Computed Styles.

    export default defineTheme({
      primary: {
        initial: '$color.blue.9',
        dark: '$color.blue.0'
      },
      blue: {
        0: '#C5CDE8',
        1: '#B6C1E2',
        2: '#99A8D7',
        3: '#7B8FCB',
        4: '#5E77C0',
        5: '#4560B0',
        6: '#354A88',
        7: '#25345F',
        8: '#161E37',
        9: '#06080F',
      },
    })
  9. Detect missing tokens in VSCode

    main

    The extension monitors your code for token usage using both the string syntax '{your.token}' and the function syntax $dt('your.token').

    If a token's origin cannot be found within your loaded Pinceau configurations, the extension will flag it as a warning in the VSCode Problems panel.

    You can customize this behavior in your VSCode settings to either change the severity level of the warning or disable the check entirely.

    // Supported syntaxes for detection:
    '{your.token}'
    $dt('your.token')
  10. Create dynamic styles with Computed Styles

    main

    To make component styles react to props, use Computed Styles. This involves two steps:

    1. Define the prop using computedStyle: Import computedStyle from pinceau/runtime and use it within defineProps. You can provide a type (e.g., keyof PinceauTheme['color']) and a default value.
    2. Use arrow functions in css(): Instead of static values, pass an arrow function to the CSS property. This function receives the props object and returns a token string.

    Example:

    <script setup lang="ts">
    import { computedStyle } from 'pinceau/runtime'
    
    defineProps({
      color: computedStyle<keyof PinceauTheme['color']>('red'),
    })
    </script>
    
    <style scoped lang="ts">
    css({
      '.my-button': {
        '--button-primary': props => `{color.${props.color}.600}`,
        '--button-secondary': props => `{color.${props.color}.500}`,
      }
    })
    </style>
  11. Overwrite local tokens using the CSS prop

    main

    The CSS prop automatically has the local tokens defined within a component injected into it. This allows parent components to easily override a component's internal design tokens by passing them through the css prop.

    <template>
      <MyButton :css="{ '--button-primary': '{color.red.200}' }" />
    </template>