Panda-CSS 🐼: Unleashing Power 🦸 and Efficiency 🤖 in Styling 💃. Pt. 2

Israel
10 min readJun 19, 2023

Introduction:

Panda-CSS is a revolutionary styling engine that combines the best of CSS-in-JS and atomic CSS paradigms, offering developers a powerful and efficient solution for styling web applications. With its advanced features and intuitive approach, Panda-CSS empowers developers to create scalable and maintainable stylesheets while optimizing performance. In this article, we will explore the key concepts and powerful features of Panda-CSS, unveiling the secrets to mastering this cutting-edge styling tool. From atomic CSS principles to readable recipes, seamless integrations, static analysis, type-safety, and best practices, we will dive into the depths of Panda-CSS, equipping you with the knowledge and techniques to elevate your styling game and build exceptional web experiences. Join me on this journey to unlock the full potential of Panda-CSS and revolutionize your approach to web styling.

Table of Content:

  • Writing Styles.
  • Conditional Styles.
  • Responsive Design.
  • Patterns.
  • Recipes.
  • Jsx Style-Props

We’ll be looking at these core concepts that makes Panda-CSS exceptional.

Let’s get to it…. 🤖

1. Writing Styles

The object syntax in Panda is essential for writing styles, offering a type-safe approach that enhances readability and ensures a consistent experience when overriding styles

Atomic Styles

In Panda, writing styles automatically generates a modern atomic stylesheet that is scoped to the @layer utilities cascade layer.

Panda exposes a css function that can be used to author styles. It accepts a style object and returns a className string.

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

const styles = css({
backgroundColor: 'gainsboro',
borderRadius: '9999px',
fontSize: '13px',
padding: '10px 15px'
})

// Generated className:
// --> bg_gainsboro rounded_9999px fs_13px p_10px_15px

<div className={styles}>
<p>Hello World</p>
</div>

This allows you to have layered styles whereby you can prioritize a particular style globally over a component or override a style in a component.

@layer utilities {
.bg_gainsboro {
background-color: gainsboro;
}

.rounded_9999px {
border-radius: 9999px;
}

.fs_13px {
font-size: 13px;
}

.p_10px_15px {
padding: 10px 15px;
}
}

The styles generated at build time will look like this:

Shorthand Properties

Panda-CSS also provides you with shorthand syntaxes to improve development time and give definitive visuals of a component style.

We’ve seen this in the likes of Tailwind CSS, Chakra UI and other frameworks that uses these syntaxes.

Here’s an example below:

// BEFORE - Good
const styles = css({
backgroundColor: 'gainsboro',
borderRadius: '9999px',
fontSize: '13px',
padding: '10px 15px'
})

// AFTER - Better
const styles = css({
bg: 'gainsboro',
rounded: '9999px',
fontSize: '13px',
p: '10px 15px'
})

Numeric values

Panda does not allow automatic convertions of numerical units. You must be definitive of your numerical units. Instead of fontSize:13, in which other frameworks 13 would be assumed in pixel.

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

// ❌ Won't work
const styles = css({
fontSize: 13,
paddingTop: 10
})

// ✅ Works
const styles = css({
fontSize: '13px',
paddingTop: '10px'
})

Nested Styles

I’m glad that Panda CSS provides the option of a nested style as it improves productivity and structure of your styles.

They went on to provide this option in native css nested styling,

<div
className={css({
bg: 'red.400',
'&:hover': {
bg: 'orange.400'
}
})}
/>

You’re also allowed to target siblings if a parent style using the & syntax

<div
className={css({
bg: 'red.400',
'& span': {
color: 'pink.400'
}
})}
/>

This is also not highly recommended especially in a large projects where you’ll consider using overrides on the global style level.

Using Pseudo Props

This is another cool feature that allows you to target specific psuedo classes. Like focus, placeholder, hover by prepending it with underscore. E.g. _focus

<div
className={css({
bg: 'red.400',
_hover: {
bg: 'orange.400'
}
})}
/>

Conditional Styling

First, Last, Odd, Even

You can style the first, last, odd, and even elements of a group using their _ modifier:

<ul>
{items.map(item => (
<li key={item} className={css({ _first: { color: 'red.500' } })}>
{item}
</li>
))}
</ul>

This replaces the nth:child concept in the native CSS which makes it more easy to track as it’s a familiar and relatable positioning word.

<ul>
{items.map(item => (
<li key={item} className={css({ _first: { color: 'red.500' } })}>
{item}
</li>
))}
</ul>

More example include:

<table>
<tbody>
{items.map(item => (
<tr
key={item}
className={css({
_even: { bg: 'gray.100' },
_odd: { bg: 'white' }
})}
>
<td>{item}</td>
</tr>
))}
</tbody>
</table>

Color Scheme

The prefers-color-scheme media feature is used to detect if the user has requested the system use a light or dark color theme.

Use the _osLight and _osDark modifiers to style an element based on the user's color scheme preference:

<div
className={css({
bg: 'white',
_osDark: { bg: 'black' }
})}
>
Hello
</div>

Orientation

The orientation media feature is used to detect if the user has a device in portrait or landscape mode.

Use the _portrait and _landscape modifiers to style an element based on the user's device orientation:

<div
className={css({
pb: '4',
_portrait: { pb: '8' }
})}
>
Hello
</div>

This is actually cool as it reduces media queries written for specific screens.

Group Selectors

When you need to style an element based on its parent element’s state or attribute, you can add the group class to the parent element, and use any of the _group* modifiers on the child element.

<div className="group">
<p className={css({ _groupHover: { bg: 'red.500' } })}>Hover me</p>
</div>

I’m also excited about this , group selection and styling of elements reduces your code and sometimes can be a headache to handle. Thumbs up for team Panda-CSS on this.

<div className="group">
<p className={css({ _groupHover: { bg: 'red.500' } })}>Hover me</p>
</div>

This modifer for every pseudo class modifiers like _groupHover, _groupActive, _groupFocus, and _groupDisabled, etc.

State Styling

You can style an element based on its data-{state} attribute using the corresponding _{state} modifier:

<div
data-loading
className={css({
_loading: { bg: 'gray.500' }
})}
>
Hello
</div>

This also works for common states like data-active, data-disabled, data-focus, data-hover, data-invalid, data-required, and data-valid.

It gets more interesting as this goes on. Knowing for a fact that this solved a state Styling problem is quite interesting as you could write styles for state without special definition of hooks or loading state.

<div
data-active
className={css({
_active: { bg: 'gray.500' }
})}
>
Hello
</div>

Note: Most of the data-{state} attributes typically mirror the corresponding browser pseudo class. For example, data-hover is equivalent to :hover, data-focus is equivalent to :focus, and data-active is equivalent to :active.

2. Responsive Design

Responsive design is a fundamental aspect of modern web development, allowing websites and applications to adapt seamlessly to different screen sizes and devices.

Panda provides five breakpoints by default:

const breakpoints = {
sm: '640px',
md: '768px',
lg: '1024px',
xl: '1280px',
'2xl': '1536px'
}

Customizing Breakpoints

When encountering certain scenarios, it may become necessary to establish custom breakpoints tailored to your application’s needs. It is advisable to utilize commonly used aliases such as sm, md, lg, and xl for this purpose.

In order to define custom breakpoints, you can easily accomplish this by passing them as an object within your Panda config.

export default defineConfig({
// ...
theme: {
extend: {
breakpoints: {
sm: '640px',
md: '768px',
lg: '1024px',
xl: '1280px',
'2xl': '1536px'
}
}
}
})

This should be done in your panda.config.ts

Note: Make sure that the CSS units of your breakpoints are consistent. Use either all pixels (px) or all em, but do not mix them.

Hiding elements by breakpoint

If you need to limit the visibility of an element to any breakpoint, Panda provides display utilities to help you achieve this.

Hide From

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

Hide Below

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

3. Patterns

Patterns are layout primitives can be used to create robust and responsive layouts with ease. Panda comes with predefined patterns like stack, hstack, vstack, wrap, etc. These patterns can be used as functions or JSX elements.
Especially for me these are one of the reasons I loved Chakra UI as it provides simplicity with those JSX elements in which increases productivity time.

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

function App() {
return (
<div className={stack({ gap: '6', padding: '4' })}>
<div>First</div>
<div>Second</div>
<div>Third</div>
</div>
)
}
import { hstack } from '../styled-system/patterns'

function App() {
return (
<div className={hstack({ gap: '6' })}>
<div>First</div>
<div>Second</div>
<div>Third</div>
</div>
)
}
import { vstack } from '../styled-system/patterns'

function App() {
return (
<div className={vstack({ gap: '6' })}>
<div>First</div>
<div>Second</div>
<div>Third</div>
</div>
)
}

Wrap

The wrap function accepts the following properties:

  • gap.
  • columnGap.
  • rowGap.
  • align == align-items.
  • justify == justify-content
import { wrap } from '../styled-system/patterns'

function App() {
return (
<div className={wrap({ gap: '6' })}>
<div>First</div>
<div>Second</div>
<div>Third</div>
</div>
)
}

You might end up writing a fully responsive website without flooding your jsx with in-line styles. Providing a clean component and great developer experience.

Usage with JSX

To use the pattern in JSX, you need to set the jsxFramework property in the config. When this is set, Panda will emit files for JSX elements based on the framework.

Every pattern can be used as a JSX element and imported from the /jsx entrypoint. The pattern name is the same as the function name, but in PascalCase.

import { VStack, Center } from '../styled-system/jsx'

function App() {
return (
<VStack gap="6" mt="4">
<div>First</div>
<div>Second</div>
<div>Third</div>
<Center>4</Center>
</VStack>
)
}

3. Recipes

Panda provides a way to write CSS-in-JS with better performance, developer experience, and composability. One of its key features is the ability to create multi-variant styles with a type-safe runtime API.

Defining the recipe

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

const button = cva({
base: {
display: 'flex'
},
variants: {
visual: {
solid: { bg: 'red.200', color: 'white' },
outline: { borderWidth: '1px', borderColor: 'red.200' }
},
size: {
sm: { padding: '4', fontSize: '12px' },
lg: { padding: '8', fontSize: '24px' }
}
}
})

This allows you to give a default value to a particular component while making it reuseable. Which encourages code composure and readability.

Using the recipe

The returned value from the cva function is a function that can be used to apply the recipe to a component. Here’s an example of how to use the button recipe:

import { button } from './button'

const Button = () => {
return (
<button className={button({ visual: 'solid', size: 'lg' })}>
Click Me
</button>
)
}

For reference on how to configure recipe you can check the docs

Best Practices when using the recipe

  • Leverage css variables in the base styles as much as possible. Makes it easier to theme the component with JS
  • Don’t mix styles by writing complex selectors. Separate concerns and group them in logical variants
  • Use the compoundVariants property to create more complex sets of styles

Style props

Style props lets you quickly build UI components in JSX by passing css properties as "props" to your components. Panda will extract the style props through static analysis and generate the CSS at build time.
While you can get very far by using the className prop and function from Panda, style props provide a more ergonomic way of expressing styles.

If you use Chakra UI, Styled System, or Theme UI, you'll feel right at home right away 😊

// The className approach
const Button = ({ children }) => (
<button
className={css({
bg: 'blue.500',
color: 'white',
py: '2',
px: '4',
rounded: 'md'
})}
>
{children}
</button>
)

// The style props approach
const Button = ({ children }) => (
<styled.button bg="blue.500" color="white" py="2" px="4" rounded="md">
{children}
</styled.button>
)

Factory Function

You can also use the styled function to create a styled component from any component or JSX intrinsic element (like "a", "button").

At this point it feels this tool has everything you could possible find in a styling framework and a native CSS.

import { styled } from '../styled-system/jsx'
import { Button } from 'component-library'

const StyledButton = styled(Button)

const App = () => (
<StyledButton bg="blue.500" color="white" py="2" px="4" rounded="md">
Button
</StyledButton>
)

JSX Patterns

Patterns are common layout patterns like stack, grid, circle that can be used to speed up your css. Think of them as a way to avoid repetitive layout styles.

All the patterns provided by Panda are available as JSX components.

import { Stack, Circle } from '../styled-system/jsx'

const App = () => (
<Stack gap="4" align="flex-start">
<button>Button</button>
<Circle size="4" bg="red.300">4</Circle>
</Stack>
)

With all that has been read and said so far. I’m mostly convinced and confident in the reliability of this Styling tool.

Well, I’m going to let us digest this. In my last series on Panda-CSS. I’ll be highlighting the following:

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

Conclusion:

Panda-CSS is not just another styling engine; it’s a game-changer in the world of web development. By combining the developer experience of CSS-in-JS with the performance benefits of atomic CSS, Panda-CSS opens up new possibilities for creating efficient, scalable, and maintainable stylesheets. Its object syntax provides a type-safe and readable style authoring experience, while the automatic generation of a modern atomic stylesheet ensures optimal performance.
With seamless integrations, static analysis capabilities, and a focus on consistency, Panda-CSS empowers developers to elevate their styling game and deliver exceptional web experiences.

Embrace the power of Panda-CSS and embark on a styling journey that will transform the way you build web applications. It’s time to unleash the full potential of your styles with Panda-CSS.

Reference:

https://panda-css.com/

Find this article helpful? Drop a like or comment.

Gracias 🙏

--

--

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.

Responses (1)