Client Area

Modern CSS in 2026 — Tailwind v4, Open Props, and CUBE CSS Architecture

ByDomain India Team·DomainIndia Engineering
6 min readPublished 23 Apr 2026Updated 23 Jun 2026299 views

In this article

  • 1The landscape today
  • 2Tailwind v4 — what's new
  • 3Setup (Vite project)
  • 4v4 migration from v3
  • 5Open Props — design tokens as CSS variables

Modern CSS in 2026 — Tailwind v4, Open Props, and CUBE CSS Architecture

TL;DR
CSS in 2026 is radically different from three years ago. Tailwind v4 compiles in milliseconds, Open Props gives you design tokens without a framework, and CUBE CSS is a sensible architecture for teams that dislike framework lock-in. This guide covers all three with practical migration paths.

The landscape today

ApproachBundle sizeDXLearning curveFit
Utility-first (Tailwind v4)Tiny with JITExcellentModerateProduct teams
Design tokens (Open Props)~5 KBGoodLowDesigners who like pure CSS
Modular architecture (CUBE CSS)VariesGreat long-termHigh initiallyLong-lived projects
CSS-in-JS (styled-components etc.)Runtime costGoodModerateReact-heavy SPAs
Vanilla CSS + modern featuresSmallestDependsLowSmall sites

No single answer. Small marketing site? Vanilla CSS. Product UI with a team? Tailwind or CUBE. Design system? Open Props.

Tailwind v4 — what's new

Released 2024, production-ready 2026:

  • Instant build — Rust-powered compiler, 5-10× faster than v3
  • Zero config for most projects — no tailwind.config.js needed, works with @import "tailwindcss"
  • CSS-first configuration — customise in CSS, not JS config file
  • Container queries built in
  • Auto content detection — scans your HTML/JS/TS without the content: array
  • Native CSS cascade layers used for predictable specificity

Setup (Vite project)

bash
npm install tailwindcss @tailwindcss/vite

vite.config.ts:

typescript
import tailwindcss from '@tailwindcss/vite';

export default {
  plugins: [tailwindcss()],
};

src/app.css:

css
@import "tailwindcss";

/* Customise inline — no separate config file */
@theme {
  --color-brand-50: oklch(0.98 0.02 260);
  --color-brand-500: oklch(0.6 0.2 260);
  --color-brand-900: oklch(0.2 0.1 260);

  --font-sans: "Inter", sans-serif;

  --spacing: 0.25rem;    /* default spacing unit */
}

Use in HTML:

html
<button class="bg-brand-500 text-white px-6 py-3 rounded-lg hover:bg-brand-900">
  Click
</button>

v4 migration from v3

bash
npx @tailwindcss/upgrade@next

Automates 90% of changes. Manual fixes:

  • ring utilities now default to 1px (was 3px)
  • bg-opacity-* → use bg-black/50 syntax
  • space-y-4 alternative: flex-col gap-4

Open Props — design tokens as CSS variables

Open Props is just a CSS file with 500+ variables. No build step, no framework, works anywhere.

bash
npm install open-props
# or via CDN
css
@import "open-props/style";
@import "open-props/normalize";

.card {
  background: var(--surface-2);
  padding: var(--size-fluid-3);
  border-radius: var(--radius-3);
  box-shadow: var(--shadow-3);
  color: var(--text-1);
}

.button {
  background: var(--blue-6);
  color: white;
  padding: var(--size-2) var(--size-4);
  border-radius: var(--radius-2);
  transition: background var(--ease-3) 150ms;
}
.button:hover {
  background: var(--blue-7);
}

Includes:

  • Colors (2500+ hues, dark mode ready)
  • Spacing (t-shirt sizes + fluid)
  • Shadows
  • Border radius
  • Animations + easings
  • Gradients
  • Media queries (var(--md) etc.)

No class pollution — just tokens. Pair with your own selectors or other frameworks.

CUBE CSS — architecture without framework

CUBE = Composition, Utility, Block, Exception. A methodology, not a library.

The four layers

  1. Composition — global layout rules (macro level)
  2. Utility — single-purpose classes for small adjustments
  3. Block — named components with their own styles
  4. Exception — rare one-off overrides via data-attributes

Example folder structure

styles/
├── composition/
│   ├── stack.css       /* vertical rhythm */
│   ├── cluster.css     /* horizontal groups */
│   ├── sidebar.css     /* content + sidebar layout */
├── utilities/
│   ├── color.css
│   ├── text.css
│   ├── spacing.css
├── blocks/
│   ├── card.css
│   ├── nav.css
│   ├── form.css
├── exceptions/         /* data-state overrides */
├── tokens.css          /* CSS custom properties */
└── main.css            /* imports all */

Stack (composition primitive)

css
.stack {
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
}
.stack > * + * {
  margin-block-start: var(--space-m);
}
html
<section class="stack">
  <h1>Welcome</h1>
  <p>Article content…</p>
  <button>Read more</button>
</section>

Every direct child gets consistent spacing. No per-component margin tweaks.

Block (component)

css
.card {
  background: var(--surface-2);
  padding: var(--space-m);
  border-radius: var(--radius-3);
}
.card__title {
  font-size: var(--font-size-3);
  font-weight: 600;
}
.card__body {
  color: var(--color-text-2);
}

Exception (state overrides)

css
[data-state="open"] { display: block; }
[data-state="closed"] { display: none; }
[data-loading="true"] .card { opacity: 0.5; pointer-events: none; }

Modern CSS features worth using in 2026

Container queries

css
.card-grid {
  container-type: inline-size;
}

.card {
  padding: var(--space-s);
}

@container (min-width: 600px) {
  .card {
    display: grid;
    grid-template-columns: 150px 1fr;
    padding: var(--space-m);
  }
}

Responsive based on parent size, not viewport. Killer for reusable components.

:has() — parent selection

css
form:has(:invalid) button[type="submit"] {
  opacity: 0.5;
  pointer-events: none;
}

CSS nesting (native)

css
.card {
  padding: var(--space-m);

  & h2 {
    font-size: var(--font-size-3);
  }

  &:hover {
    transform: translateY(-2px);
  }
}

No preprocessor needed.

color-mix() and oklch

css
:root {
  --brand: oklch(0.6 0.2 260);
  --brand-lighter: color-mix(in oklch, var(--brand) 70%, white);
}

oklch gives perceptually-uniform colors — no more "muddy" gradients.

@scope (limited support 2026)

css
@scope (.card) {
  h2 { color: var(--brand); }
}

Styles only apply inside the scope. Cleaner than BEM for component isolation.

Which to choose?

Migration tips

From Bootstrap → Tailwind

  • Replace grid classes: col-md-6md:w-1/2
  • Forms: use @tailwindcss/forms plugin
  • Components: build your own; copy from ui.shadcn.com (free)

From CSS-in-JS → vanilla + tokens

  • Extract colors, spacing, fonts to CSS variables
  • Move component styles to CSS files
  • Tree-shake styled-components/emotion packages

Common pitfalls

FAQ

Q Tailwind or vanilla CSS for small site?

For <10 pages with no team: vanilla CSS + Open Props is faster to write and understand. Tailwind shines when you have many pages or components to keep consistent.

Q Does Tailwind v4 work with WordPress / Laravel / Rails?

Yes — it's a CSS tool, framework-agnostic. Configure the build step to watch your templates.

Q Performance impact of Tailwind?

JIT purges to only classes you use. Typical production CSS is 15-30 KB gzipped. Smaller than Bootstrap.

Q Should I use CSS-in-JS in 2026?

Declining trend. Modern browser features (scoping, nesting, container queries) + vanilla CSS + tokens cover most cases without runtime cost.

Q Dark mode implementation?

color-scheme: light dark + prefers-color-scheme media query. Use CSS variables; flip values in @media (prefers-color-scheme: dark). Tailwind v4 supports dark: prefix automatically.

Modern CSS runs anywhere — ship your next site on DomainIndia. Pick a plan

Was this article helpful?

Your feedback helps us improve our documentation

Still need help? Submit a support ticket