with one click
client-tailwind-v4
Use when styling apps/client with Tailwind CSS v4.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
Use when styling apps/client with Tailwind CSS v4.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Based on SOC occupation classification
Use when adding, changing, extracting, or reusing client components, including Storybook stories and controls.
Use when adding or changing forms in apps/client.
Use when changing GitHub OAuth, session cookies, SessionGuard, logout, or authenticated API request identity.
Use when adding or changing routes in apps/client.
Use when adding or changing client state, React Query data flow, immutable context values, or Zustand stores.
Use when adding or changing client API query or mutation hooks.
| name | client-tailwind-v4 |
| description | Use when styling apps/client with Tailwind CSS v4. |
Generated from ai/registry.json. Do not edit manually.
Canonical skill: ../../../ai/skills/client-tailwind-v4.md.
Referenced context:
../../../ai/rules/client-styling-rules.md../../../ai/rules/client-component-rules.md../../../ai/architecture/client-app.md../../../ai/examples/good-client-component.mdThis file is compiled from canonical AI knowledge files. Edit canonical files under ai, then run npm run ai:sync.
ai/skills/client-tailwind-v4.mdUse this skill when styling apps/client with Tailwind CSS v4.
Preserve the existing product visual language while using Tailwind utilities and responsive behavior correctly.
ai/rules/client-styling-rules.mdai/rules/client-component-rules.mdai/architecture/client-app.mdai/examples/good-client-component.mdclassName utilities.npm --workspace @capture-flag/client run build.ai/rules/client-styling-rules.mdRules for Tailwind CSS v4 and class composition in apps/client.
@tailwindcss/vite and @import "tailwindcss".styles.css with CSS custom properties and @theme inline when they are true app-wide tokens.classnames package for conditional class composition.classnames as cls with import cls from "classnames";.cls("base", { "active": isActive }).cls when appending or composing conditional class sets.classNames, cn, or wrapper helpers without a concrete repeated need.styles.css when component utility classes can express the behavior.npm --workspace @capture-flag/client run build after styling changes.ai/rules/client-component-rules.mdRules for React component boundaries in apps/client.
src/components.src/core/<category>/<name>.ts, with one exported function or hook per file.src/layouts/<LayoutName>.src/pages.src/pages/<PageName> when they are not shared outside that page.src/core utilities and hooks from their direct alias file path such as @core/json/formatJson; do not add index.ts barrels under src/core.@components/Button; do not assume a central src/components/index.ts barrel exists.apps/client at or below 400 lines; when touching larger existing files, prefer splitting real UI responsibilities instead of expanding them further.children for layout wrappers such as cards, shells, and empty states.className, aria-invalid, and ref.apps/client.stories/ child folder next to the component folder they cover, using *.stories.tsx; route/panel grouping stories belong in the owning route folder's stories/ folder.src/components/stories and member component stories in src/components/members/stories.src/layouts/<LayoutName>/stories.src/pages/<PageName>/stories or src/pages/<PageName>/<section>/stories.src/pages/stories.src/stories; do not put component stories there.src/core.args and argTypes updates.*.stories.tsx beside component files when a nearby stories/ folder is available..map() when rendering API data, dynamic collections, long repeated groups, or lists whose members are not all known at author time.src/core/date, src/core/json, src/core/strings, src/core/validation, or src/core/hooks only for helpers that are independent of Capture Flag domain context.src/core/<category>/__tests__/<name>.test.ts.@core/json/formatJson over barrels or grouped core imports.*.stories.tsx file per component or cohesive route grouping.src/stories/mockData.ts when stories need realistic Capture Flag data..storybook/preview.tsx for route and panel stories that call client API hooks.parameters.router.initialEntries when a story depends on React Router params.parameters.layout = "fullscreen" for route, layout, shell, and panel stories that need app-width context.npm --workspace @capture-flag/client run storybook:build after Storybook, component, or story changes.npm --workspace @capture-flag/client run build after component moves.ai/architecture/client-app.mdapps/client is a Vite React application for the Capture Flag platform UI.
src/main.tsx owns top-level providers.src/router.tsx owns React Router route definitions.lazyRoute() helper and should expose named exports.src/layouts contains route layout wrappers that render shared shells, navigation, headers, and nested <Outlet /> regions.src/pages contains route-level screens. Multi-file route screens use folder modules with index.ts named exports; simple one-file screens may stay as direct page files.src/components contains shared UI used by multiple pages or sections.src/core contains context-independent client utilities and reusable hooks organized by category.src/api contains client request functions, React Query hooks, operation barrels, domain barrels, and domain query keys.src/routing contains route path and route context helpers shared by pages and layouts.src/stories contains shared Storybook fixtures and API mocks, not component stories.src/test contains shared Vitest and Testing Library helpers.PlatformLayout owns the authenticated shell, navigation frame, sidebar/header state, logout flow, and nested route outlet.useRouteContext, not stored in a mutable layout context./: redirects to /organizations./login: GitHub login screen./account: authenticated user account details and display name editing./organizations and /organizations/:organizationId: organization selection and organization members./organizations/:organizationId/projects and /organizations/:organizationId/projects/:projectId: project selection and project members./organizations/:organizationId/projects/:projectId/environments: environments for the selected project./organizations/:organizationId/projects/:projectId/configs and /organizations/:organizationId/projects/:projectId/configs/:configId: configs and public Config JSON preview./organizations/:organizationId/projects/:projectId/configs/:configId/flags: feature flags and remote config values./organizations/:organizationId/projects/:projectId/configs/:configId/segments: reusable targeting segments./organizations/:organizationId/projects/:projectId/sdk-keys: SDK key lifecycle for project configs/environments, with selected config/environment in ?configId= and ?environmentId= when needed./organizations/:organizationId/audit-logs: organization/project audit log timeline, with selected project in ?projectId= when needed.*: redirects to /.src/api/<domain>/<operation>.useQuery or useInfiniteQuery according to the API shape.useRouteContext for selected resources and redirect-safe navigation paths.src/layouts/<LayoutName> until reused by another layout or page.src/components and are imported directly through aliases such as @components/Button.components/members primitives with page-specific role options.stories/ child folder beside the source area they cover.src/pages/stories.src/stories and are reused by stories and page tests.src/core/<category>/<name>.ts.date, json, strings, validation, and hooks.src/core/date/toDate.index.ts barrels under src/core; multiple helpers require multiple explicit imports.src/core/<category>/__tests__/<name>.test.ts next to the category they cover.__tests__/ child folders beside the source area they cover, mirroring Storybook stories/ folders.src/test and provide Testing Library render helpers, React Query providers, and fetch response mocks.npm --workspace @capture-flag/client run test:coverage and should stay at 90% or higher for configured client targets.src/api/__tests__ and cover operation behavior through mocked fetch responses.src/components/__tests__; member component tests live in src/components/members/__tests__.src/pages/<PageName>/__tests__ when they cover one page folder.src/pages/<PageName>/<section>/__tests__ when they cover colocated page internals such as feature flags or segments.src/__tests__ only for source files owned directly by src, such as permissions.ts.src/components/stories; member component stories live in src/components/members/stories.src/layouts/<LayoutName>/stories.stories/ child folder.src/pages/stories and use route parameters plus shared mock data to render realistic page states.src/stories; that folder is reserved for shared Storybook data, routes, and API mocks.ai/examples/good-client-component.mdSource: apps/client/src/components/Button.tsx (sha256: 7e1bcb1c45c5d1a5610f108be5028ce68d34ded2c394d326428eca08702a992d)
Source: apps/client/src/components/Panel.tsx (sha256: c0dfcd9d6984e9741af7abe91c60ad8bcb30e4d8f18c3c1456bdea5882d2472f)
Why this is canonical:
classnames as cls for optional classes.Canonical shared component patterns from apps/client/src/components.
import cls from "classnames";
import type { ComponentPropsWithoutRef } from "react";
type ButtonVariant = "primary" | "secondary" | "danger" | "ghost";
const baseButtonClassName =
"inline-flex h-9 shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-md px-4 py-2 text-sm font-medium shadow-xs outline-none transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4";
const buttonClassNames: Record<ButtonVariant, string> = {
danger:
"border border-transparent bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20",
ghost:
"border border-transparent bg-transparent text-foreground shadow-none hover:bg-accent hover:text-accent-foreground",
primary: "border border-transparent bg-primary text-primary-foreground hover:bg-primary/90",
secondary:
"border border-border bg-background text-foreground hover:bg-accent hover:text-accent-foreground",
};
type ButtonProps = ComponentPropsWithoutRef<"button"> & {
variant?: ButtonVariant;
};
export function Button({ className, variant = "primary", ...props }: ButtonProps) {
return (
<button className={cls(baseButtonClassName, buttonClassNames[variant], className)} {...props} />
);
}
This accepts native button props, keeps variants explicit, and uses cls for optional classes.
export function Panel({ children, className, showTitle = true, title, wide = false }: PanelProps) {
return (
<section
className={cls("grid gap-4 text-foreground", className, {
"lg:col-span-2": wide,
})}
>
{showTitle ? <h2 className="text-xl font-semibold tracking-tight">{title}</h2> : null}
{children}
</section>
);
}
This keeps layout composition explicit through children, accepts optional native composition through className, and preserves product visual language.