Panda CSSšŸ¼: The concluding part; Is it too early, Or itā€™s the GOAT of CSS frameworks?

Israel
9 min readJun 20, 2023

Introduction

In this concluding part, weā€™ll talk about theming, utilities, tokens, customization regarding what inspired Panda-CSS and my honest opinion about it so far.

Itā€™s new, room for improvements are available in all aspect of their amazing features. Nobody wants a problem solving tool that creates another problem. I believe in continuous integration and performance check. This way the product continues to scale greatly and deliver great developers experience.

Table of Content

  • Theming
  • Customization
  • Utilities
  • Guides.
  • References.

1. Theming

Design Tokens

Design tokens are the ultimate design decision managers for your application or website, working seamlessly across platforms. They are like a treasure trove of attributes that define the fundamental building blocks of visual style. With each attribute representing a key-value pair, design tokens provide a powerful and flexible way to control and maintain your design system consistently. Say goodbye to scattered styles and embrace the harmonious world of design tokens!

A design Tokens consist of the following attributes:

  • value: The value of the token. This can be any valid CSS value.
  • description: An optional description of what the token can be used for.

Letā€™s configure a token in the panda.config.js file.

export default defineConfig({
theme: {
// šŸ‘‡šŸ» Define your tokens here
tokens: {
colors: {
primary: { value: '#0FEE0F' },
secondary: { value: '#EE0F0F' }
},
fonts: {
body: { value: 'system-ui, sans-serif' }
}
}
}
})

In native CSS this practice is similar to defining css variables inĀ :root which is in your global css file. Hereā€™s an example of itā€™s useage.

import { css } from '../styled-system/css'

function App() {
return (
<p
className={css({
color: 'primary',
fontFamily: 'body'
})}
>
Hello World
</p>
)
}

Token Nesting

This is also interesting being able to nest your styles while conditionally rendering them without going through much logic.

Nested tokens create a hierarchical structure, grouping related tokens together for enhanced organization and flexibility.

export default defineConfig({
// ...
theme: {
semanticTokens: {
colors: {
bg: {
DEFAULT: { value: '{colors.gray.100}' },
muted: { value: '{colors.gray.100}' }
}
}
}
}
})

Note: You can use the DEFAULT key to define the default value of a nested token

Below is the usage of it after definition.

import { css } from '../styled-system/css'

function App() {
return (
<div
className={css({
// šŸ‘‡šŸ» This will use the `DEFAULT` value
bg: 'bg',
// šŸ‘‡šŸ» This will use the `muted` value
color: 'bg.muted'
})}
>
Hello World
</div>
)
}

Gradients

Gradient tokens embody seamless color transitions, defined as either strings or composite values.

type Gradient =
| string
| {
type: 'linear' | 'radial'
placement: string
colors: Array<string | { color: string; position: number }>
}
const theme = {
tokens: {
gradients: {
// string value
simple: { value: 'linear-gradient(to right, red, blue)' },
// composite value
primary: {
value: {
type: 'linear',
placement: 'to right',
colors: ['red', 'blue']
}
}
}
}
}

This code snippet defines a theme object with a "gradients" property that contains two gradient tokens: "simple" and "primary". The "simple" token has a string value representing a linear gradient from red to blue. The "primary" token has a composite value defined as an object with a type of "linear", a placement of "to right", and an array of colors consisting of red and blue. The "colors" array allows for additional customization by specifying colors with positions. The type "Gradient" is defined as either a string or an object with specific properties for type, placement, and colors.

Tokens can be used for various css properties such as:

  • size, shadow, borders, radii, lineHeight
  • fontsĀ , fontFamily, fontWeight, fontSize
  • spacing, assets, z-index, animations, durations

Hereā€™s an example using size token:

Size tokens represent the width and height of an element. Its value is defined as a string.

const theme = {
tokens: {
sizes: {
sm: { value: '12px' }
}
}
}

Token Helpers

To help defining tokens in a type-safe way, you can use the following helpers:

import { defineTokens } from '@pandacss/dev'

const theme = {
tokens: defineTokens({
colors: {
primary: { value: '#ff0000' }
}
})
}

You can also use this function to define tokens in a separate file:

import { defineTokens } from '@pandacss/dev'

export const colors = defineTokens.colors({
primary: { value: '#ff0000' }
})

2. Customization

Pattern Configuration

Panda allows customization of built-in and custom patterns, enabling the creation of personalized layout abstractions for your application.
This helps a lot while building enterprise softwares as it creates structured style reusablility. Letā€™s see how this works.

const config = {
patterns: {
extend: {
scrollable: {
description: 'A container that allows for scrolling',
properties: {
// The direction of the scroll
direction: { type: 'enum', value: ['horizontal', 'vertical'] },
// Whether to hide the scrollbar
hideScrollbar: { type: 'boolean' }
},
// disallow the `overflow` property (in TypeScript)
blocklist: ['overflow'],
transform(props) {
const { direction, hideScrollbar, ...rest } = props
return {
overflow: 'auto',
height: direction === 'horizontal' ? '100%' : 'auto',
width: direction === 'vertical' ? '100%' : 'auto',
scrollbarWidth: hideScrollbar ? 'none' : 'auto',
WebkitOverflowScrolling: 'touch',
'&::-webkit-scrollbar': {
display: hideScrollbar ? 'none' : 'auto'
},
...rest
}
}
}
}
}
}

To create a pattern, you can use theĀ patternsĀ property in the config. Letā€™s say we want to create a "Scrollable" pattern that applies preset styles to a container that allows for scrolling.

Then you can run the following command to generate the pattern JS code:

npm panda codegen

Now you can import the pattern and use it in your application:

import { scrollable } from './styled-system/patterns'

const App = () => {
return (
<div className={scrollable({ direction: 'vertical', hideScrollbar: true })}>
<div>Scrollable content</div>
</div>
)
}

Thereā€™s quite a bunch to explore under customization but itā€™s better explained in the documentation. Iā€™ll drop reference at the end of this article so you can get more dirts on your hand šŸ’»šŸ§‘ā€šŸ’».

Utility

The utility API is a way to create your own CSS properties, map existing properties to a set of values or tokens. Itā€™s like building your own typesafe version of Chakra UI, Tailwind (in JS), or Styled System.

Panda comes with a set of utilities out of the box. You can customize them, or add your own.

  • classNameĀ : The className the property maps to
  • shorthand: The shorthand or alias version of the property
  • values: The possible values the property can have. Could be a token category, or an enum of values, string, number, or boolean.
  • transform: A function that converts the value to a valid css object

How can you create a custom utility? Letā€™s say we want to create new property br that applies a border radius to an element.

export default defineConfig({
utilities: {
extend: {
br: {
className: 'rounded', // css({ br: "sm" }) => rounded-sm
values: 'radii', // connect values to the radii tokens
transform(value) {
return { borderRadius: value }
}
}
}
}
})

Always remember to generate the pattern js code as we did when creating tokens.

npm panda codegen

How can we use this new utility createdĀ inĀ ourĀ component?

function App() {
return <div className={css({ br: 'sm' })} />
}

Or in a jsx file.

function App() {
return <styled.div br="sm" />
}

3. Utilities

Panda provides the following utilities or style properties for styling background colors, gradients, and images.

Background Colors

<div className={css({ bg: 'red.200' })} />

Background Gradients

<div
className={css({
bgGradient: 'to-r',
gradientFrom: 'red.200',
gradientTo: 'blue.200'
})}
/>

Properties to create a background gradient based on color stops.

Background Position

Properties for controlling the src and position of a background image.

<div
className={css({
bgImage: 'url(/images/bg.jpg)',
bgPosition: 'center'
})}
/>

Panda provides the following utilities or style properties for styling text.

Font Properties

<div className={css({ fontFamily: 'mono' })} />

Line Clamp (Truncation)

<div className={css({ lineClamp: 2 })}>Some long piece of text</div>

<div className={css({ lineClamp: 2 })}>Truncated text</div>

This helps truncate multi-line text.

Text Styles

Utilities for applying a composition of typography properties.

<h1
className={css({
textStyle: 'heading/marketing'
})}
>
Hello World
</h1>

Panda provides the css properties for styling borders

Border Radius

All sides

<div className={css({ borderRadius: 'md' })} />
<div className={css({ rounded: 'md' })} /> // shorthand

Specific sides

<div className={css({ borderTopRadius: 'md' })} />
<div className={css({ roundedTop: 'md' })} /> // shorthand

<div className={css({ borderLeftRadius: 'md' })} />
<div className={css({ roundedLeft: 'md' })} /> // shorthand

Specific corners

<div className={css({ borderTopLeftRadius: 'md' })} />
<div className={css({ roundedTopLeft: 'md' })} /> // shorthand

Logical Props

Panda also provides the logical properties for border radius, which map to corresponding physical properties based on the documentā€™s writing mode.

<div className={css({ borderStartRadius: 'md' })} />
<div className={css({ roundedStart: 'md' })} /> // shorthand

For example, borderStartRadius will map to border-left-radius in LTR mode, and border-right-radius in RTL mode.

Panda provides the following utilities or style properties for styling display of an element.

Display Property

<div className={css({ display: 'flex' })} />

Hiding Elements.

Panda provides shortcut properties for hiding elements from and below a specific breakpoint.
Hide From

<div className={css({ display: 'flex', hideFrom: 'md' })} />

Hide below

<div className={css({ display: 'flex', hideBelow: 'md' })} />

This props helps with media screen conditional display.

4. Guides

Dynamic styling

While Panda is mainly focused on the statically analyzable styles, you might need to handle dynamic styles in your project.

Runtime values

Using a value that is not statically analyzable at build-time will not work in Panda due to the inability to determine the style values.

import { css } from '../styled-system/css'

const App = () => {
const [color, setColor] = useState('red.300')

return (
<div
className={css({
// āŒ Avoid: Panda can't determine the value of color at build-time
color
})}
/>
)
}

The example above will not work because Panda canā€™t determine the value of color at build-time. Here are some ways to fix this:

Using Static CSS

Panda supports a staticCss option in the config you can use to pre-generate some styles ahead of time.

import { defineConfig } from '@pandacss/dev'

export default defineConfig({
staticCss: {
css: [{
properties: {
// āœ… Good: Pre-generate the styles for the color
color: ['red.300']
}
}]
}
})
import { useState } from 'react'
import { styled } from '../styled-system/jsx'

export const Button = () => {
const [color, setColor] = useState('red.300')

// āœ… Good: This will work because `red.300` is pre-generated using `staticCss` config
return <styled.button color={color} />
}

In summary for dynamic styling letā€™s look at the list of practice you can implement and Iā€™ll highlight a bit of what code structure isnā€™t allowed.

What can you do?

// āœ… Good: Conditional styles
<styled.div color={{ base: "red.100", md: "red.200" }} />

// āœ… Good: Arbitrary value
<styled.div color="#121qsd" />

// āœ… Good: Arbitrary selector
<styled.div css={{ ["&[data-thing] > span": { color: "red.100" } }} />

// āœ… Good: Runtime value (with config.`staticCss`)
const Button = () => {
const [color, setColor] = useState('red.300')
return <styled.button color={color} />
}

// āœ… Good: Runtime condition
<styled.div color={{ base: "red.100", md: isHovered ? "red.200" : "red.300" }} />

// āœ… Good: Referenced value
<styled.div color={mainColor} />

What you canā€™t do

// āŒ Avoid: Runtime value (without config.`staticCss`)
const Button = () => {
const [color, setColor] = useState('red.300')
return <styled.button color={color} />
}

// āŒ Avoid: Referenced value (not statically analyzable or from another file)
<styled.div color={getColor()} />
<styled.div color={colors[getColorName()]} />
<styled.div color={colors[colorFromAnotherFile]} />

Multi-Theme Tokens

Panda supports advance token definition beyond just light/dark mode; theming beyond just dark mode. You can define multi-theme tokens using nested conditions.

Let's say your application supports a pink and blue theme, and each theme can have a light and dark mode. Let's see how to model this in Panda.

Weā€™ll start by defining the following conditions for these theme and color modes:

const config = {
conditions: {
light: '[data-color-mode=light] &',
dark: '[data-color-mode=dark] &',
pinkTheme: '[data-theme=pink] &',
blueTheme: '[data-theme=blue] &'
}
}

Next, weā€™ll define a colors.text semantic token for the pink and blue theme.

const theme = {
// ...
semanticTokens: {
colors: {
text: {
value: {
_pinkTheme: '{colors.pink.500}',
_blueTheme: '{colors.blue.500}'
}
}
}
}
}

Next, weā€™ll modify colors.text to support light and dark color modes for each theme.

const theme = {
// ...
semanticTokens: {
colors: {
text: {
value: {
_pinkTheme: { base: '{colors.pink.500}', _dark: '{colors.pink.300}' },
_blueTheme: { base: '{colors.blue.500}', _dark: '{colors.blue.300}' }
}
}
}
}
}

Now, you can use the text token in your styles, and it will automatically change based on the theme and the color scheme.

// use pink and dark mode theme
<html data-theme="pink" data-color-mode="dark">
<body>
<h1 className={{ color: 'text' }}>Hello World</h1>
</body>
</html>

// use pink and light mode theme
<html data-theme="pink">
<body>
<h1 className={{ color: 'text' }}>Hello World</h1>
</body>
</html>

5. Reference

https://panda-css.com/docs/references/cli

https://panda-css.com/docs/references/config

Hereā€™s a playground link to test through what this awesome tool brought to the styling community.

Find this article helpful? Drop a like and comment.

Happy Coding šŸ’»šŸ§‘ā€šŸ’»!

--

--

Israel
Israel

Written by Israel

I'm Isreal a Frontend Engineer with 4+ experience in the space . My love to profer solutions led me to being a technical writer. I hope to make +ve impact here.

No responses yet