본문으로 건너뛰기
cccskills
무료GitHub에 공개

web-styling-tailwind

Tailwind CSS v4 - utility-first CSS framework with CSS-first configuration

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

설치 방법 보기

포함된 파일(5)

  • SKILL.md16.4 KB
  • examples/advanced.md21.1 KB
  • examples/core.md20.2 KB
  • metadata.yaml535 B
  • reference.md6.6 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Tailwind CSS v4+ Patterns

Quick Guide: v4 is configured in CSS, not JavaScript: @import "tailwindcss" replaces the three @tailwind directives, and @theme replaces tailwind.config.js — each variable in it generates utility classes and a real CSS custom property. Custom utilities come from @utility and custom variants from @custom-variant; the v3 plugin API and @layer utilities no longer reach the variant system. Source files are detected automatically, so there is no content array. Several defaults moved: borders are currentColor, ring is 1px, the shadow and radius scales shifted one step, and the important modifier trails (flex!).

Detailed Resources:

  • examples/core.md — setup and source detection, responsive design, the three dark-mode strategies, state variants, @theme customisation
  • examples/advanced.md — @utility, @custom-variant, container queries, 3D transforms, @starting-style, masks, class composition
  • reference.md — @theme namespace table, v3-to-v4 differences, renamed utilities, browser support

Which path applies

  • A new project — install the package for your bundler, add @import "tailwindcss", and put every token in @theme. Start at examples/core.md.
  • A v3 project being upgraded — run npx @tailwindcss/upgrade first; it converts the config, the directives and most renamed utilities. The tables in reference.md cover what it leaves, and custom plugins have to become @utility and @custom-variant by hand — see examples/advanced.md.

<critical_requirements>

Before writing Tailwind code

Configure in CSS: @import "tailwindcss" and @theme. v4 does not look for tailwind.config.js at all unless a @config directive points at it, and the three @tailwind directives no longer exist.

Declare custom utilities with @utility. That registers them with the variant system, so hover: and lg: reach them — a @layer utilities block produces plain classes that no variant can prefix.

Install the package that matches the bundler — @tailwindcss/vite, @tailwindcss/postcss, @tailwindcss/webpack or @tailwindcss/cli. tailwindcss itself is no longer a PostCSS plugin, and vendor prefixing and import inlining are built in, so autoprefixer and postcss-import come out.

Give @theme colours a wide-gamut value such as oklch(). The default palette is oklch, so a hex brand colour is clamped to sRGB and reads as flat beside it.

State border and ring values explicitly. v4 defaults a border to currentColor and a ring to 1px, so markup carried over from v3 renders with the text colour and a thinner ring than it asked for.

</critical_requirements>


Auto-detection: @import "tailwindcss", @theme, @theme inline, @utility, @custom-variant, @variant, @source, @reference, @slot, --value(), --modifier(), --spacing(), @tailwindcss/vite, @tailwindcss/postcss, @tailwindcss/cli, tailwind-merge, @container, dark:, group-hover:, peer-checked:, data-[state=…]:

Applies to:

  • Styling with utility classes directly in markup
  • Declaring theme tokens in CSS so they generate both classes and custom properties
  • Responsive layout with viewport breakpoints and with container queries
  • Dark mode by media query, class, or data attribute
  • Adding utilities and variants the framework does not ship
  • Migrating a v3 project to v4

Handled elsewhere:

  • The token architecture behind the theme entries — how values are tiered, named and generated for other platforms. @theme is where a token becomes a class, not where the set is designed
  • How a theme is chosen, persisted and applied before first paint — the dark variant decides what a theme looks like, not which one is active
  • Turning a component's props into a table of class combinations — cn() composes a list; where the list is defined is a separate concern
  • Scoped hand-written stylesheets — @reference makes the theme reachable from inside one, and the scoping mechanism itself is settled elsewhere

<philosophy>

Utility-first, and in v4 also CSS-native. Styles are composed from single-purpose classes in the markup, and the design decisions those classes encode live in CSS rather than in a JavaScript config — which means the theme is readable by the browser as well as by the build, since every @theme entry emits a custom property alongside the classes it generates.

That dual role is the thing to hold on to. --color-brand-500 in @theme is simultaneously a token any stylesheet can read through var() and the source of bg-brand-500, text-brand-500 and the rest of the namespace. Most of v4's sharper edges — why inline exists, why @theme cannot be nested, why a bare channel triplet breaks opacity modifiers — follow from that one variable serving both purposes.

</philosophy>

<decision_framework>

Decision framework

Dark mode strategy

Should the theme follow the operating system and nothing else?
|-- YES --> Nothing to configure. `dark:` already means prefers-color-scheme: dark.
|-- NO  --> A user-facing toggle is needed. What carries the state?
    |-- A class on <html>          --> @custom-variant dark (&:where(.dark, .dark *))
    |-- A data attribute on <html> --> @custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *))

Where a design decision goes

Should it produce a utility class?
|-- YES --> @theme. Reference another variable? Then @theme inline.
|-- NO  --> Is it a whole declaration block reused across components?
    |-- YES --> @utility, so variants can still prefix it
    |-- NO  --> :root, read directly with var() — no class is generated

Responsive to what?

The viewport      --> sm: md: lg: breakpoint variants
The element's own container --> @container on the parent, then @sm: @md: on the child
Neither           --> unprefixed utilities

</decision_framework>


<patterns>

Core patterns

Pattern 1: CSS-first setup

One import, one theme block, and the bundler's own plugin. There is no content array — source files are detected automatically, respecting .gitignore.

@import "tailwindcss";

@theme {
  --color-brand-500: oklch(0.7 0.15 250);
  --font-display: "Satoshi", "sans-serif";
}

@source adds a path automatic detection misses (node_modules is skipped by default), @source not excludes one, and @source inline("…") safelists classes assembled at runtime that no scan can find. @reference "../app.css" makes the theme available inside a scoped stylesheet without duplicating its output.

Full code: examples/core.md


Pattern 2: Responsive design

Mobile-first: unprefixed utilities apply everywhere, and each breakpoint variant adds an override above its width. Custom breakpoints are @theme entries.

<div
  class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4"
></div>
@theme {
  --breakpoint-3xl: 120rem;
}

Stack variants for a range (md:max-lg:hidden), and use a max-* variant where a rule genuinely applies below a width rather than above one. Reaching for max-* throughout is desktop-first, which needs an override per breakpoint instead of one.

Full code: examples/core.md


Pattern 3: Dark mode

dark: follows prefers-color-scheme with no configuration. A user-facing toggle means overriding the variant so it reads a class or an attribute instead.

@import "tailwindcss";

/* Class-based toggle */
@custom-variant dark (&:where(.dark, .dark *));

/* Or attribute-based */
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));

For a large surface, semantic theme entries beat a dark: pair on every utility: the components name a role, and the mode swap happens once in the stylesheet.

@theme inline {
  --color-surface: var(--surface);
}
:root {
  --surface: oklch(0.99 0 0);
}
.dark {
  --surface: oklch(0.15 0 0);
}

Full code: examples/core.md


Pattern 4: State variants

Pseudo-class variants style the element itself; group-* styles a child from an ancestor's state and peer-* from a sibling's; data-[…] styles from an attribute the component sets.

<div class="group rounded-lg p-4 hover:shadow-lg">
  <h3 class="text-gray-900 group-hover:text-blue-600">Title</h3>
</div>

<input class="peer" />
<p class="hidden peer-invalid:block">Please enter a valid email</p>

<button data-state="active" class="data-[state=active]:border-b-2">Tab</button>

Name a group or peer (group/card, group-hover/card:) wherever they nest, or the inner one silently answers to the outer. not-* inverts a condition — not-hover:opacity-75 replaces a base value plus a hover override.

Full code: examples/core.md


Pattern 5: Theme customisation with @theme

Every entry generates utilities in its namespace and a custom property. --color-*: initial clears one namespace before yours; --*: initial clears the whole default theme.

@theme {
  --color-brand-500: oklch(0.55 0.2 250);
  --font-display: "Satoshi", sans-serif;
  --radius-card: 1rem;
  --shadow-card: 0 2px 8px oklch(0 0 0 / 0.08);
  --ease-fluid: cubic-bezier(0.3, 0, 0, 1);
}

@theme inline resolves a referenced variable into each generated utility, which is what lets a value reassigned on .dark or [data-theme] reach the classes. @theme static emits the custom properties even for utilities nobody used. The namespace-to-utility table is in reference.md.

Full code: examples/core.md


Pattern 6: Custom utilities with @utility

@utility registers a utility with the variant system. A trailing -* makes it functional, and --value() decides what it accepts — theme entries, bare values, or bracketed arbitrary ones, in cascade order.

@utility content-auto {
  content-visibility: auto;
}

@utility tab-* {
  tab-size: --value(--tab-size-*); /* a theme entry */
  tab-size: --value(integer); /* a bare value: tab-4 */
  tab-size: --value([integer]); /* arbitrary: tab-[6] */
}

--modifier() handles the optional /… suffix, and an omitted modifier drops its declaration entirely. --spacing() multiplies the theme's spacing unit.

Full code: examples/advanced.md


Pattern 7: Custom variants with @custom-variant

The shorthand takes a selector; the block form takes rules with @slot marking where the utility's own declarations land.

@custom-variant theme-ocean (&:where([data-theme="ocean"] *));

@custom-variant any-hover {
  @media (any-hover: hover) {
    &:hover {
      @slot;
    }
  }
}

&:where() keeps the variant's specificity at zero, so a variant never wins on specificity alone. Inside hand-written CSS, @variant dark { … } applies an existing variant without @apply.

Full code: examples/advanced.md


Pattern 8: Container queries

Built in, no plugin. @container makes an element a query container, and @sm:, @md: on its descendants respond to its width rather than the viewport's.

<div class="@container">
  <div class="flex flex-col gap-4 @sm:flex-row @lg:gap-6">…</div>
</div>

Name the container (@container/sidebar, then @sm/sidebar:) wherever containers nest. @max-* queries a maximum width and @min-md:@max-xl: a range.

Full code: examples/advanced.md


Pattern 9: Modern CSS the framework exposes

v4 puts a set of platform features behind ordinary utilities, so they compose with every variant.

<!-- 3D: perspective on the parent, transform-3d on the child -->
<div class="perspective-distant">
  <div
    class="transform-3d transition-transform hover:rotate-x-2 hover:rotate-y-3"
  >
    …
  </div>
</div>

<!-- @starting-style, for an entrance with no JavaScript -->
<div
  popover="auto"
  class="transition-all open:opacity-100 starting:open:opacity-0"
>
  …
</div>

<!-- A textarea that grows with its content -->
<textarea class="field-sizing-content"></textarea>

bg-linear-45 and /oklch cover angled gradients and perceptual interpolation; mask-* covers gradient masking; text-shadow-* covers text shadows; pointer-coarse: and pointer-fine: size controls for the input device.

Full code: examples/advanced.md


Pattern 10: Composing a class list

cn() — conditional joining plus conflict resolution — lets a component expose a className that can genuinely override its own utilities.

import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";

function cn(...inputs: ClassValue[]): string {
  return twMerge(clsx(inputs));
}
// The caller's className goes last, so it wins the merge
className={cn("rounded-lg px-4 py-2", VARIANT_CLASSES[variant], className)}

Prefer this to @apply, which moves the utilities out of the markup into a CSS layer that is then harder to override than the classes it replaced.

Full code: examples/advanced.md

</patterns>

<red_flags>

Red flags

v3 syntax that silently produces nothing:

  • @tailwind base; @tailwind components; @tailwind utilities; — the directives are gone. @import "tailwindcss" is the one replacement.
  • tailwind.config.js with no @config — v4 does not look for it, so the file reads as configured while none of it applies.
  • @layer utilities { .foo { … } } — produces a class, but the variant system never sees it, so hover:foo and lg:foo do not exist.
  • tailwindcss as a PostCSS plugin — moved to @tailwindcss/postcss. Alongside it, autoprefixer and postcss-import are now redundant.
  • addVariant() through the plugin API — replaced by @custom-variant.
  • !flex and bg-[--var] — the important modifier trails now (flex!) and a CSS variable takes parentheses (bg-(--var)).

Breaks the theme:

  • @theme nested inside a selector or a media query — it is top-level only, because the classes it generates are emitted at build time and cannot be conditional on a runtime scope. Mode-conditional values go in :root and [data-theme]; only the alias goes in @theme.
  • Omitting inline on an aliasing theme entry — the generated utility carries a chained reference resolved where the theme entry was declared, not where the class is used, so the [data-theme="dark"] reassignment never reaches it. No build warning, no console message, and the page stays in light mode.
  • A bare channel triplet as a theme colour (0 0% 100%) — an opacity modifier compiles to color-mix(in oklab, var(--color-x) 50%, transparent), which needs a real colour, so the declaration is dropped and only the modified utilities break.
  • The same value typed in :root and in @theme — two copies of one decision, free to drift, with the mismatch showing as a subtle colour difference between utility-styled and directly-styled elements.
  • Hex colours in @theme — clamped to sRGB beside an oklch default palette.

Changed defaults that look like a regression:

  • border alone is currentColor, not a grey. State the colour.
  • ring is 1px; the v3 default is ring-3.
  • The shadow, radius and blur scales each shifted a step: v3's shadow is v4's shadow-sm, and v3's shadow-sm is shadow-xs.
  • outline-none became outline-hidden.
  • Variant stacking reads left to right, following the cascade — the reverse of v3.
  • hover: only applies where the device actually has hover, so a touch device no longer keeps a hover state after a tap.
  • space-y-* and divide-* changed selector: gap-* with flex or grid is the more predictable choice.

Surprising behaviour:

  • Automatic source detection skips node_modules, so classes inside an installed component library need an explicit @source.
  • A class assembled at runtime is invisible to detection. @source inline("…") is the safelist.
  • Grid template values separate with underscores, not commas: grid-cols-[max-content_auto].
  • @apply inside a scoped stylesheet needs @reference first, or the theme is not in scope.
  • v4 needs Safari 16.4+, Chrome 111+ and Firefox 128+; there is no build target that reaches older engines.
  • Sass, Less and Stylus are not supported — v4 is itself the preprocessor.

</red_flags>

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Hugging Face Inference SDK patterns for TypeScript/Node.js — InferenceClient setup, chat completion, text generation, streaming, embeddings, image generation, audio transcription, translation, summarization, and Inference Endpoints

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

agents-inc/skills252026년 10월 17일 업데이트

LiteLLM proxy server setup, TypeScript client patterns via OpenAI SDK, model routing, fallbacks, load balancing, spend tracking, virtual keys, and production deployment

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

agents-inc/skills252026년 10월 17일 업데이트

Serverless GPU compute platform for AI model deployment — web endpoints, GPU functions, model serving, and TypeScript client patterns

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

agents-inc/skills252026년 10월 17일 업데이트

Local LLM inference with the Ollama JavaScript client -- chat, streaming, tool calling, vision, embeddings, structured output, model management, and OpenAI-compatible endpoint

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

agents-inc/skills252026년 10월 17일 업데이트

Replicate SDK patterns for TypeScript/Node.js -- client setup, predictions, streaming, webhooks, file handling, model versioning, deployments, and training

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

agents-inc/skills252026년 10월 17일 업데이트

Together AI SDK patterns for TypeScript — client setup, chat completions, streaming, structured output, function calling, embeddings, image generation, fine-tuning, and OpenAI-compatible endpoints

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

agents-inc/skills252026년 10월 17일 업데이트

agents-inc의 스킬 모두 보기

이 스킬의 문제 신고