Skip to main content
ASoc
Tutorial

This Storefront Ships Zero Tailwind Plugins — Here's the 74 Lines Instead

No @plugin line in globals.css, no @tailwindcss/forms or typography. A hand-rolled container, marquee keyframes, and no-scrollbar cover what plugins usually do.

The ASoc Team8 min read

This codebase ships zero Tailwind CSS plugins. grep "@plugin" src/app/globals.css returns nothing, and package.json lists exactly one Tailwind package that isn't the framework itself — @tailwindcss/postcss, the build integration, not a plugin. Every custom behavior a plugin would normally add here is instead 74 lines of hand-written CSS in one file.

The short answer

A Tailwind plugin is a JavaScript function, registered with @plugin in Tailwind v4 (or the plugins array in v3's config), that adds new utility classes, variants, or base styles beyond what core Tailwind ships. The two official first-party plugins still outside core are @tailwindcss/forms (a form-element reset) and @tailwindcss/typography (the prose class for rendering rich text). Several other former plugins — container queries, aspect-ratio, line-clamp — were folded directly into core over Tailwind's last few major versions and no longer need a plugin at all.

What this repo actually has instead of plugins

/* src/app/globals.css */
@import "tailwindcss";

@custom-variant dark (&:where(.dark, .dark *));

@theme {
  /* --color-*, --font-*, --breakpoint-* tokens */
}

.container { /* custom responsive container, 7 breakpoints */ }

@keyframes marquee {
  from { transform: translateX(0); }
  to { transform: translateX(-100%); }
}

@layer utilities {
  .no-scrollbar::-webkit-scrollbar { display: none; }
  .no-scrollbar {
    -ms-overflow-style: none;
    scrollbar-width: none;
  }
}

Three things in that file each correspond to a real, commonly-installed plugin — and each one is written by hand instead:

What it doesPlugin that would normally do thisLines in globals.css
Hides a scrollbar on horizontal carouselstailwind-scrollbar-hide7
A custom responsive .container (padding + 7 breakpoint max-widths)Tailwind's own container plugin (deprecated in v4 in favor of the pattern used here)36
A marquee keyframe animation for the logo carouseltailwindcss-animate8
Class-based dark mode via @custom-variantNothing — this is core v4's own mechanism1

None of these needed a package install. .no-scrollbar is used on exactly the carousel-style components that need it (MarqueeRow, TemplateGallery's thumbnail rail); a plugin would have added the utility globally whether or not the rest of the app ever touched it.

Why zero, not "not yet"

The two remaining official plugins map cleanly onto features this storefront doesn't have:

  • @tailwindcss/typography generates a prose class tuned for arbitrary long-form HTML — the classic use case is a CMS or Markdown body with no per-element control. This codebase's blog article typography is the opposite: every element the MDX pipeline can produce (h2, p, table, code, pre, blockquote…) is styled individually in src/mdx-components.tsx, because — as that file's own comment states — "the rest of the app styles every element at its call site, so there is no global prose stylesheet to inherit." A prose class would fight that convention rather than serve it.
  • @tailwindcss/forms resets native <select>/<input>/<textarea> chrome so utility classes can restyle them predictably. This repo's forms — NewsletterForm, the auth pages' email/password fields, the contact form — are few enough, and styled consistently enough by hand, that a global reset plugin has nothing to standardize across. Adding it now would mean auditing every existing form input for a visual regression, for a benefit (consistent baseline across dozens of forms) this catalog-sized form count doesn't need yet.

What Tailwind v4 already folded into core

Part of why this project needs no plugins is that several once-common ones no longer exist as plugins at all:

Former pluginNowLanded
@tailwindcss/aspect-ratioCore aspect-* utilitiesv3.4
@tailwindcss/line-clampCore line-clamp-* utilitiesv3.3
Community container-query pluginsCore @container + @sm:/@md: variantsv4.0
Manual dark: config wiring@custom-variant dark (used above)v4.0

A "best Tailwind plugins" list written for Tailwind v3 is quietly obsolete on two of its usual four entries in v4 — the class still works, but installing the plugin now just adds a redundant dependency for something core already does.

The one place a plugin might have made sense — and why it still didn't

The .container block is the strongest candidate for "why not use the built-in mechanism": Tailwind ships a container utility class of its own, and pre-v4 projects commonly configured it via the theme.container key or the deprecated @tailwindcss/container behavior. This repo's version needs padding that changes independently of its max-width at the 1440px breakpoint (padding-inline: 4rem with no matching max-width change) — a shape the built-in utility's single padding key can't express per-breakpoint. Seven hand-written @media blocks cost 36 lines once, in a file every component already reads for its design tokens; keeping the mismatch in a config file would have split the definition across two places for one utility class.

If a plugin becomes necessary later

Nothing above is a permanent ban — it's a description of what this catalog needs today. If a future template edition needs a genuinely large form surface (a multi-step checkout with dozens of native inputs, say), @tailwindcss/forms earns its place the moment hand-styling each input individually costs more than the one-time visual audit of adding a global reset. The decision rule that keeps this honest: a plugin is worth adding when it replaces repeated hand-written CSS, not when it merely offers a class name for something used once. .no-scrollbar stayed hand-written specifically because it's needed in exactly two places; a plugin would have been the wrong trade for two call sites.

The mechanics of adding one in this repo's v4 setup are a single line in globals.css, immediately after the @import:

@import "tailwindcss";
@plugin "@tailwindcss/forms";

No tailwind.config.js is involved — that file doesn't exist in this project's v4 CSS-first setup, and a v3-style plugins: [require("@tailwindcss/forms")] array would have no effect here even if one were added back. Whatever registers a plugin has to live in the CSS Tailwind actually compiles.

Troubleshooting

SymptomCauseFix
@plugin directive has no effectTailwind v4 loads plugins from @import "tailwindcss" plus explicit @plugin "package-name"; lines — a plugin installed via npm but never referenced in CSS never registersAdd the @plugin line in the same file as the @import, not just the package.json dependency
A v3-era tailwind.config.js plugins: [] array does nothingv4's CSS-first config (@theme in globals.css) replaces the JS config file for this project — see the framework note in CLAUDE.mdMove plugin registration to @plugin in globals.css, or use the compatibility layer if migrating from v3
Installing @tailwindcss/aspect-ratio for aspect-video throws a "duplicate utility" style warningThe utility is already built into v4 coreDelete the plugin dependency; the class still works with no plugin installed
A community plugin's utilities don't show up after npm installTurbopack (this repo's dev/build engine) needs the plugin declared as a CSS @plugin, not just imported in JSConfirm the plugin's own docs support the CSS-first v4 API before adding it here
Restyling a <select> element fights browser default chromeNo forms-reset plugin is installed, so native appearance (arrows, padding) still appliesEither add @tailwindcss/forms project-wide, or reset the one element by hand with appearance-none plus manual padding, matching how this repo's existing inputs are styled

Frequently asked questions

Does this project use Tailwind's official plugins at all? No — zero @plugin lines in globals.css, and no @tailwindcss/* package beyond the required @tailwindcss/postcss build tool. Every customization (container widths, marquee animation, scrollbar hiding, dark mode) is hand-written CSS in one 133-line file.

Is skipping plugins a performance optimization? Indirectly. Tailwind only generates the utility classes actually referenced in your source, so an unused plugin costs nothing in output size. The real reason to skip one here is fit, not bytes: none of the two remaining official plugins (forms, typography) match how this codebase already styles its forms and its MDX article body.

Would @tailwindcss/forms break anything if added today? It would change the default appearance of every native form element site-wide the moment it's registered, because its reset applies globally, not per-component. That's a visual audit across every existing input before merge — the cost this post's "why not yet" section is describing.

How do I know if a plugin I'm considering is still necessary in v4? Check whether the feature it promises is now core. aspect-ratio, line-clamp, and container queries moved into core across v3.3–v4.0; a plugin promising any of those today is solving an already-solved problem.

Templates in this post

ASoc Vault, ASoc Vox and ASoc Weave are Next.js + Tailwind landing page templates built on the same plugin-free globals.css foundation audited above — every marquee, container, and dark-mode toggle on these pages runs on the hand-rolled CSS shown here, not an installed dependency.

Browse the full sets: Next.js landing page templates, Tailwind landing page templates.

Keep reading

Tutorial11 min read

Scroll-Driven Animations in Tailwind v4 Without a JS Library

animation-timeline replaces the scroll-animation library category — behind two guards. The Tailwind v4 setup, the element you must never fade in, and when IntersectionObserver still wins.

Read more
Tutorial11 min read

Multi-Tenant Theming with Tailwind CSS v4 and CSS Variables

Tailwind v4 tokens compile to real CSS custom properties, so one build can serve every tenant's brand. The override pattern, contrast handling, and the pitfalls.

Read more