
shadcn/ui hit 110,000 GitHub stars in early 2026 and is the most-watched UI library in the JavaScript ecosystem. It did not get there by having the most components or the best documentation. It got there by solving a design system problem that every serious project faces: how do you ship components that are genuinely yours, styled for your brand, maintainable by your team, not locked into a vendor’s release cycle, without rebuilding everything from scratch?
WordPress block theme developers face an identical problem. How do you build a theme that has a coherent design language, uses a proper token system, is maintainable across WordPress updates, and can be customised per-project without rebuilding the core structure? The answer shadcn/ui found for React applies, principle for principle, to theme.json and block theme architecture. This post unpacks those principles and shows how to apply them.
The Direct Equivalents: shadcn/ui vs Block Themes
Before going into each principle, here is a mapping of the core shadcn/ui concepts to their WordPress block theme equivalents. The architectural parallel is closer than most WordPress developers realize:
| shadcn/ui Concept | WordPress Block Theme Equivalent | Where Defined |
|---|---|---|
CSS custom properties (--primary, --radius) | Palette, fontSize, spacing tokens | theme.json |
| Component files in your codebase | Block patterns in patterns/ | Theme directory |
| Theme variants (dark mode, brand) | Style variations | styles/ directory |
| Primitive components (Button, Card) | Core blocks (Group, Buttons, Cover) | WordPress core |
| Composed page layouts | Block templates | templates/ directory |
| Radix UI primitives underneath | Block editor primitives | WordPress core / Gutenberg |
| Tailwind config (design scale) | theme.json settings object | theme.json |
Every abstraction shadcn/ui uses has a direct parallel in the block theme architecture. The question is whether block theme authors use these tools with the same intentionality that shadcn/ui does. Most do not, not because the tools are missing, but because the discipline has not been explicitly articulated for the WordPress context. The rest of this post does exactly that.
What shadcn/ui Actually Is (And Why It Is Different)
Most UI libraries work by giving you a package you install and import from. Your components live in node_modules. When the library ships a breaking change, you update the package and fix the breakage. You are a consumer of someone else’s code.
shadcn/ui works differently. You copy components into your own codebase. They become your code. You own the implementation. When something needs to change, the button radius, the color token, the hover state, you change it directly, in your codebase, with no abstraction layer between you and the output. shadcn/ui describes this as “open code”: the design system is not a dependency, it is a starting point.
This is a philosophical shift from “use our components” to “here is a well-designed implementation, make it yours.” And it is exactly the philosophy that WordPress block themes should adopt, and in several ways already can, but do not always.
Principle 1: Design Tokens First, Components Second
The foundation of shadcn/ui is a token layer defined in CSS custom properties. Every color, radius, spacing value, and shadow is a named token before it is applied to any component. The button does not have a hardcoded background color, it references --primary. The card does not have a hardcoded border radius, it references --radius. Change the token, and every component that uses it updates automatically.
WordPress block themes have exactly this capability in theme.json. Every color you define in the palette array becomes a CSS custom property: --wp--preset--color--primary. Every font size in the fontSizes array, every spacing value in the spacing.spacingSizes array, all CSS custom properties, automatically generated, available to every block. The mechanism is identical to what shadcn/ui built. The difference is that most block themes do not use it this way.
The common pattern in block themes is to define colors in theme.json and then override them with inline styles added through the Site Editor. Those inline styles are not tokens, they are hardcoded values in block markup that do not update when the theme palette changes. The token system exists in theme.json, but the moment you use the Site Editor to set a specific hex color directly on a block, you have bypassed it.
The shadcn/ui discipline applied to block themes: define your full design token set in theme.json before building anything. Colors, typography scale, spacing scale, border radii. Use only tokens, never hardcoded values, in block markup and patterns. When a site needs a colour change, it requires changing one token in theme.json, not hunting through block markup. The theme.json deep dive covers how to structure this token layer properly.
Principle 2: Copy, Do Not Import
shadcn/ui’s “copy into your codebase” model means the implementation is always inspectable, always modifiable, and never broken by an upstream update you did not control. There is no magic, no hidden behaviour, no vendor-specific abstraction. The component is just code in your project.
Block patterns in WordPress work this way natively, or should. A well-built pattern library is a set of block markup templates that live in your theme’s patterns/ directory, registered via PHP, owned entirely by the theme. No plugin dependency, no external template loader, no “this pattern requires Plugin X to be active.” The pattern is code. It is yours.
The failure mode in block theme development is the opposite: patterns that depend on specific plugin widgets, blocks registered by third-party plugins, or Site Editor templates that are stored in the database rather than in the theme’s file structure. These are all “import from a dependency” rather than “own the implementation.” When the plugin updates, the pattern breaks. When the database-stored template diverges from the file-based template, you get inconsistencies that are difficult to debug.
The practical discipline: your theme’s core layout patterns should be file-based, in the theme directory, with no required plugin dependencies. Plugin-dependent patterns are acceptable as optional enhancements, but the foundational layout system should be self-contained. This is the block theme equivalent of shadcn/ui’s copy model.
Principle 3: Composition Over Configuration
shadcn/ui components are built from small, composable primitives. A Dialog is composed of DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription. You assemble what you need; you do not configure what to show or hide on a monolithic component. This composability means you can build any layout variation without fighting against the component’s assumptions.
Block themes get composability through the pattern system and through template parts. A page layout is not a monolithic template, it is a composition of header template part, content area with specific inner blocks, footer template part, and optional sidebar pattern. The Site Editor makes this composition visual, which is the right model.
Where block themes fall into the configuration trap is when theme authors try to make patterns too flexible by adding excessive variation settings, multiple header layouts controlled by theme options, different footer styles selected from a settings panel, section background colours set through the Customizer. This is configuration of a monolithic structure, not composition of small parts. The shadcn/ui lesson: build more patterns, fewer options. A theme with fifteen layout patterns covering different use cases is more flexible and easier to maintain than a theme with three patterns and forty configuration options.
Principle 4: Semantic Naming, Not Visual Naming
shadcn/ui names its tokens semantically: --primary, --secondary, --destructive, --muted. Not --blue-500, --red-400. The token name describes the role, not the visual value. This matters because the role stays constant when the design changes, --destructive is always the colour for destructive actions, whether it is red today and coral tomorrow.
Most block themes name their palette entries visually: primary, secondary if they are thoughtful, but more often dark-blue, light-gray, brand-orange. When the brand changes from orange to teal, you now have a token called brand-orange that maps to a teal value, which is worse than just having the wrong color, because it actively misleads anyone reading the code.
The fix is straightforward in theme.json: name palette entries by role. primary, primary-foreground, secondary, secondary-foreground, accent, surface, surface-foreground, border, destructive. Assign whatever colour values the current design requires. When the design changes, you update the values while the names stay constant. Every block that references --wp--preset--color--primary updates automatically, and the name remains semantically correct. The design system guide for theme.json covers how to implement this token naming system in a production theme.
Principle 5: Style Variations as Themes, Not Configuration
One of the most elegant shadcn/ui features is the theming model: swap the token values, change the entire visual appearance. Dark mode is not a separate component set, it is the same components with different token values applied via a CSS class. This makes dark mode, seasonal themes, and brand variations lightweight to implement and easy to maintain.
WordPress block themes have direct support for exactly this model: style variations. A style variation is a theme.json-compatible file in styles/ that overrides specific design tokens. Users can switch between style variations from the Site Editor, and developers can ship multiple variations as first-class supported configurations of a single theme. Dark mode is a style variation that overrides the color tokens. A high-contrast accessibility variant is a style variation. A seasonal rebrand is a style variation.
The key requirement for this to work cleanly, and the place where the shadcn/ui discipline is essential, is that the style variation can only override tokens. If your patterns and templates use hardcoded colors instead of token references, the style variation cannot change them. Token-first design, applied consistently through the theme, is what makes style variations work as intended. This is the same foundational discipline as shadcn/ui’s CSS custom property layer, you only get the benefit if you use it consistently.
A Practical Checklist: Is Your Block Theme Following These Principles?
- Token audit: Open your theme’s block patterns and check the block attributes. Do you see hex colors directly in attributes (
"backgroundColor":"#1a1a2e"), or theme palette slugs ("backgroundColor":"primary")? Hardcoded values mean you have not fully adopted the token model. - Naming audit: Review your
theme.jsonpalette entries. Are any named for their visual value (dark-blue,light-gray) rather than their semantic role (surface,muted)? Rename them before the theme ships, it is harder to rename after blocks reference the old slugs. - Pattern ownership: List all patterns your theme uses. Which ones require a plugin to be active? Those are dependencies you could eliminate by building the pattern with native blocks. Eliminate plugin dependencies from foundational layout patterns.
- Style variation test: If your theme has or plans style variations, create a minimal dark-mode variation that only overrides color tokens. If the site still shows hardcoded colours after applying it, you have found token violations to fix.
- Composition check: Count your theme options and settings panels. Are you providing configuration for things that could instead be separate patterns? Each configuration option that could be a pattern is a maintenance burden you can eliminate.
What a shadcn/ui-Inspired theme.json Palette Looks Like
Here is the concrete difference between a visually-named palette (what most themes ship) and a semantically-named, shadcn/ui-inspired palette (what they should ship). Both produce the same visual output. Only one is maintainable.
Typical theme.json palette (visual naming, avoid this)
dark-navy→#1a1a2ebright-orange→#ff6b35light-gray→#f5f5f5pure-white→#fffffftext-dark→#222222
shadcn/ui-inspired palette (semantic naming, do this)
primary→#1a1a2e(main brand color for backgrounds, CTAs)primary-foreground→#ffffff(text/icons on primary backgrounds)accent→#ff6b35(highlights, hover states, emphasis)surface→#f5f5f5(card backgrounds, section fills)foreground→#222222(default body text)border→#e2e2e2(dividers, input borders)muted→#6b7280(secondary text, captions)
When the brand pivots from navy to forest green next year, the semantic version requires changing three hex values in theme.json. The visually-named version requires renaming slugs, which breaks every block and pattern that references the old slug, or keeping misleading names like dark-navy mapped to a green value. Semantic naming is not extra work upfront; it is avoided work later. The token-to-production guide has a full reference implementation of this pattern.
Where WordPress Block Themes Still Fall Short
To be fair to the block theme ecosystem: the tools are not the problem. WordPress ships a capable token system, a pattern library model, and a style variation mechanism. The gaps are in tooling and conventions, not architecture.
No standardized token vocabulary
shadcn/ui ships with a standardized set of semantic token names that everyone using it adopts. There is no equivalent standard for block themes, every theme invents its own palette slug names. This makes it hard to build tooling around tokens and impossible to write patterns that work across different themes without knowing the specific token names each theme uses. The WordPress community would benefit from a conventional token vocabulary, even a non-enforced one.
Site Editor stores overrides in the database
When a user customises a block theme through the Site Editor, those customisations are stored in the wp_posts table as a wp_global_styles post, not in the theme files. This means the canonical source of truth for the theme’s appearance is split between the filesystem and the database, which creates problems for version control, deployment, and handoff. shadcn/ui’s model, everything in your codebase, version controlled, deployable, has no equivalent in the Site Editor workflow. This is a WordPress architecture limitation, not something theme developers can fully solve today.
Pattern inheritance is not well-supported
shadcn/ui’s copy model means you can easily create a variant of a component by copying it and modifying the copy. Block patterns do not have a composable inheritance model, you cannot “extend” a pattern the way you extend a class. Pattern Overrides in WordPress 7.0 partially address this for synced patterns, but the broader pattern composition story is still immature compared to what component libraries in React have achieved.
Frequently Asked Questions
Do I need to know React or JavaScript to apply these principles?
No. The principles are architectural, they are about how you structure your token layer, name your palette entries, and organise your patterns. Everything described in this post is implemented in theme.json and PHP pattern files. No JavaScript required.
Can I apply these principles to an existing block theme?
Yes, incrementally. Start with the palette: audit your theme.json and rename any visually-named tokens to semantic names. Update your pattern files to reference the new slugs. Then audit for hardcoded color values in block markup and replace them with token references. This is the highest-leverage improvement and can be done without changing the visual design at all. The composition and ownership principles can follow in subsequent releases.
Is shadcn/ui coming to WordPress directly?
Not as a direct port, but the influence is there. The Gutenberg team has looked at the design system work happening in the JavaScript ecosystem, and some of the theme.json decisions, particularly around the spacing scale and the structured token format, show clear design system thinking. The WordPress Design System (WPDS) used in the admin interface follows similar semantic token conventions. The gap is between the admin-facing design system and the theme-facing public API, they use the same principles but there is no official guidance for theme developers to follow the same conventions.
How does this relate to the WordPress Pattern Overrides feature in 7.0?
Pattern Overrides in WordPress 7.0 address the rigidity of synced patterns, previously, a synced pattern was identical everywhere it was used, with no way to override specific content per-instance. With Pattern Overrides, you can designate specific blocks within a synced pattern as overridable, so each instance can have different text or images while the layout stays locked. This moves block themes meaningfully closer to shadcn/ui’s composition model: a synced pattern with overrides is conceptually similar to a shadcn/ui component where you pass different props per instance. The structure (component/pattern) is shared; the content (props/overrides) varies per use. For theme developers building reusable layout components, testimonial cards, pricing tables, feature sections, Pattern Overrides make the “own your implementation, vary per instance” model genuinely practical in a way it was not before 7.0.
The Deeper Lesson
shadcn/ui became the most-starred UI library in the JavaScript ecosystem not because it shipped the most features but because it solved a real architectural problem with a clean model. The design system principles it demonstrated, tokens first, semantic naming, owned implementations, composition over configuration, are not React-specific ideas. They are good software design applied to UI systems.
WordPress block themes have the technical foundations to implement all of these principles natively. theme.json is a first-class token system. The patterns directory is an owned implementation model. Style variations are the theme-switching model. The Site Editor is the composition interface. The architecture is there. What is often missing is the discipline to use it consistently, the same discipline that made shadcn/ui successful in the JavaScript ecosystem.
Block theme developers who apply these principles will build themes that are more maintainable, easier to hand off to clients, easier to adapt for different brands, and more resilient to WordPress updates. That is the same outcome shadcn/ui delivers for React developers. The tools are in theme.json, the question is whether you use them with the same intentionality.
Three Things to Do This Week
- Rename your palette. Open
theme.jsonand rename every colour entry to a semantic name,primary,secondary,surface,on-surface,muted. This single change makes every pattern and style variation you write hereafter immediately more portable and readable. - Audit one synced pattern. Pick your most-used synced pattern and identify which blocks should be overridable with Pattern Overrides. Enabling overrides on just the heading and image blocks gives you a reusable layout component instead of a static stamp.
- Write one style variation. Create a minimal
styles/dark.jsonthat remaps your semantic palette to a dark colour scheme. If your token names are semantic,surface,primary, the variation file will be under 30 lines. If it takes 100 lines, your palette naming needs the work from step one first.
Start with the token layer. Name your palette entries semantically before you build a single pattern. That one discipline, choosing primary over dark-blue, surface over light-gray, compounds into every pattern you write, every style variation you ship, and every site rebrand your clients will eventually ask for. It is the most impactful ten minutes you will spend on a block theme, and it is the principle that shadcn/ui got right before it got famous. The full implementation guide for this token layer is in the theme.json deep dive, start there, apply what you find, and build from solid foundations.
