From b5b65faad4dc3d126192c0149e89a4beda87d5fd Mon Sep 17 00:00:00 2001 From: Vivek Date: Thu, 23 Jul 2026 11:54:07 +0530 Subject: [PATCH 01/73] feat: ship @webjsdev/ui class-helper primitives into the gallery scaffold Add components/ui/{button,card,input,textarea,badge}.ts (the ui tier-1 class helpers buttonClass/cardClass/inputClass/textareaClass/badgeClass) to the gallery template, with the cn import rewritten to the scaffold's #lib/utils/cn.ts alias. First step of the gallery design-system refactor (#1059). --- .../templates/gallery/components/ui/badge.ts | 46 ++++++++++ .../templates/gallery/components/ui/button.ts | 80 +++++++++++++++++ .../templates/gallery/components/ui/card.ts | 90 +++++++++++++++++++ .../templates/gallery/components/ui/input.ts | 38 ++++++++ .../gallery/components/ui/textarea.ts | 29 ++++++ 5 files changed, 283 insertions(+) create mode 100644 packages/cli/templates/gallery/components/ui/badge.ts create mode 100644 packages/cli/templates/gallery/components/ui/button.ts create mode 100644 packages/cli/templates/gallery/components/ui/card.ts create mode 100644 packages/cli/templates/gallery/components/ui/input.ts create mode 100644 packages/cli/templates/gallery/components/ui/textarea.ts diff --git a/packages/cli/templates/gallery/components/ui/badge.ts b/packages/cli/templates/gallery/components/ui/badge.ts new file mode 100644 index 000000000..894c222d5 --- /dev/null +++ b/packages/cli/templates/gallery/components/ui/badge.ts @@ -0,0 +1,46 @@ +/** + * Badge: small visual label. Tier-1 class helper; compose with any + * inline element (commonly `` or `` for linked badges). + * + * shadcn parity: + * Badge (variant: default | secondary | destructive | outline | ghost | link) + * → badgeClass({ variant }) + * + * The `[a&]:hover:...` hover styles only apply when the element is an ``, + * so a static `` doesn't pick up an unwanted hover. + * + * A11y (required for accessible output): render a static badge as a plain + * (not focusable, no tabindex). Only an interactive badge (an + * or + * + * + * About + * ``` + */ +import { cn } from '#lib/utils/cn.ts'; + +// cursor-pointer is on the BASE so every variant (default, outline, +// ghost, link, …) gets the right hover affordance. Native + * + * + *
+ *

Your weekly digest is ready to review.

+ *
+ *
+ * + *
+ * + * ``` + */ + +export type CardSize = 'default' | 'sm'; + +/** + * Card root. shadcn ships `size?: "default" | "sm"` on Card across + * 14/15 style families (only new-york-v4 omits it). The class string + * uses `group/card` so the header / title / content / footer helpers + * can read the parent card's data-size and adjust their own padding + * + gap. + * + * USAGE: pass size to cardClass AND set data-size="" on the + * same host element so the group-data-[size=...]/card child rules + * fire. Set `data-slot="card"` for shadcn parity. + * + *
+ *
...
+ * ... + *
+ * + * Sizes: + * default: gap-6 / py-6 (shadcn new-york-v4 default) + * sm: gap-3 / py-3 (shadcn radix-nova + base-* defaults) + */ +export const cardClass = (opts: { size?: CardSize } = {}): string => { + const size = opts.size ?? 'default'; + const base = + 'group/card flex flex-col rounded-xl border bg-card text-card-foreground shadow-sm'; + return size === 'sm' ? base + ' gap-3 py-3' : base + ' gap-6 py-6'; +}; + +/** + * Card header: supports an optional `CardAction` slot via grid layout. + * group-data-[size=sm]/card rules pick up the compact layout when the + * root card carries data-size="sm". + */ +export const cardHeaderClass = (): string => + '@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6 group-data-[size=sm]/card:px-4 group-data-[size=sm]/card:gap-1 group-data-[size=sm]/card:[.border-b]:pb-3'; + +/** Card title: heading text within the header. Smaller when card is data-size="sm". */ +export const cardTitleClass = (): string => + 'leading-none font-semibold group-data-[size=sm]/card:text-sm'; + +/** Card description: subdued caption beneath the title. */ +export const cardDescriptionClass = (): string => 'text-sm text-muted-foreground'; + +/** Card action: right-aligned controls inside the header (matches shadcn CardAction). */ +export const cardActionClass = (): string => + 'col-start-2 row-span-2 row-start-1 self-start justify-self-end'; + +/** Card content: the main body region. Tighter padding when card is data-size="sm". */ +export const cardContentClass = (): string => + 'px-6 group-data-[size=sm]/card:px-4'; + +/** Card footer: trailing controls or actions. Tighter padding when card is data-size="sm". */ +export const cardFooterClass = (): string => + 'flex items-center px-6 [.border-t]:pt-6 group-data-[size=sm]/card:px-4 group-data-[size=sm]/card:[.border-t]:pt-3'; diff --git a/packages/cli/templates/gallery/components/ui/input.ts b/packages/cli/templates/gallery/components/ui/input.ts new file mode 100644 index 000000000..e8cf541ec --- /dev/null +++ b/packages/cli/templates/gallery/components/ui/input.ts @@ -0,0 +1,38 @@ +/** + * Input: styled native ``. Tier-1 class helper. Works with every + * input type (text, email, password, number, search, tel, url, date, + * time, file, color, …). Form submission, autocomplete, browser + * validation, and password managers all work because it IS the native + * input. + * + * shadcn parity: + * Input → inputClass() + * + * Pair with `