Skip to content

feat(shadcn): #49 add button/card/skeleton/avatar/progress#56

Closed
fray-cloud wants to merge 2 commits into
devfrom
shadcn/49-components
Closed

feat(shadcn): #49 add button/card/skeleton/avatar/progress#56
fray-cloud wants to merge 2 commits into
devfrom
shadcn/49-components

Conversation

@fray-cloud

@fray-cloud fray-cloud commented Apr 13, 2026

Copy link
Copy Markdown
Owner

Summary

Stacked on #55. Five shadcn/ui components written directly into apps/web/src/new-component/ui/ (matches the alias configured in #48). Standard default-style implementations.

  • button.tsx — variants (default/destructive/outline/secondary/ghost/link), sizes (default/sm/lg/icon), asChild via @radix-ui/react-slot, class-variance-authority
  • card.tsxCard + CardHeader/CardTitle/CardDescription/CardContent/CardFooter
  • skeleton.tsxanimate-pulse bg-muted placeholder
  • avatar.tsx — Radix Avatar/Image/Fallback ("use client")
  • progress.tsx — Radix Progress with translate-X indicator ("use client")
  • Deps: @radix-ui/react-slot, @radix-ui/react-avatar, @radix-ui/react-progress

No call-site swaps — files are unreferenced until #50#54 consume them. daisyUI still live.

Closes #49.

Test plan

  • npx nx affected -t lint test build --exclude web-e2e green

🤖 Generated with Claude Code

Summary by Sourcery

Add initial shadcn-style UI foundation and components for the web app.

New Features:

  • Introduce shared utility for merging Tailwind class names.
  • Add shadcn-style Button, Card, Avatar, Progress, and Skeleton UI components under the new component library path.

Enhancements:

  • Configure Tailwind theme tokens and dark-mode support for shadcn-style design system.
  • Extend Tailwind with shadcn-compatible color variables, border radii, and animation plugin integration.

Build:

  • Add Radix UI, Tailwind, and utility dependencies required for the new shadcn-style components.

Handcrafted shadcn init to preserve the front/* alias and Nx layout.
Scaffolding only — no call-site swaps; daisyUI plugin remains active.

- apps/web/components.json — shadcn schema 1, style default, rsc true,
  baseColor neutral, cssVariables, aliases mapped onto front/*
  (components → front/new-component, ui → front/new-component/ui,
   utils → front/lib/utils, lib → front/lib, hooks → front/hooks),
  iconLibrary lucide
- apps/web/src/lib/utils.ts — cn helper (clsx + tailwind-merge)
- apps/web/src/new-component/ui/ directory created (empty; #49 fills)
- apps/web/tailwind.config.js — darkMode ['class'], theme.extend.colors
  wired to hsl(var(--*)) shadcn tokens, theme.extend.borderRadius,
  plugins keep `require('daisyui')` and add `tailwindcss-animate`
- apps/web/app/globals.css — :root + .dark CSS variables under
  @layer base
- package.json — class-variance-authority, clsx, tailwind-merge,
  tailwindcss-animate, lucide-react added

- nx affected -t lint test build --exclude web-e2e green
  (4 projects, 10 tasks)
- daisyUI classes still render identically (coexistence by design
  through #54)

Refs #48
Five shadcn/ui components written directly into
apps/web/src/new-component/ui/ to match the aliases configured in
#48. Standard shadcn default-style implementations using cn(),
class-variance-authority (button only), and Radix primitives for
Avatar and Progress. No call-site swaps — files are unreferenced
until #50#54 consume them.

- button.tsx — variants (default/destructive/outline/secondary/ghost/
  link), sizes (default/sm/lg/icon), asChild via @radix-ui/react-slot
- card.tsx — Card + CardHeader/Title/Description/Content/Footer
- skeleton.tsx — animated muted placeholder
- avatar.tsx — Radix Avatar/Image/Fallback ("use client")
- progress.tsx — Radix Progress with translate-based indicator
  ("use client")
- package.json — @radix-ui/react-slot, @radix-ui/react-avatar,
  @radix-ui/react-progress added

- nx affected -t lint test build --exclude web-e2e green

Refs #49
@vercel

vercel Bot commented Apr 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
animal-project Error Error Apr 13, 2026 8:41am

@sourcery-ai

sourcery-ai Bot commented Apr 13, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds initial shadcn/ui-style design system primitives (button, card, skeleton, avatar, progress) plus supporting Tailwind theme tokens and utilities, wired for class-based dark mode and Radix UI, without yet replacing any call sites.

Class diagram for new shadcn UI primitives and utilities

classDiagram
  class cn {
    +cn(inputs) $Returns string$
  }

  class Button {
    +asChild boolean
    +variant string
    +size string
    +ButtonProps
  }

  class buttonVariants {
    +buttonVariants(options) $Returns string$
  }

  class Card {
    +Card
  }

  class CardHeader {
    +CardHeader
  }

  class CardTitle {
    +CardTitle
  }

  class CardDescription {
    +CardDescription
  }

  class CardContent {
    +CardContent
  }

  class CardFooter {
    +CardFooter
  }

  class Avatar {
    +Avatar
  }

  class AvatarImage {
    +AvatarImage
  }

  class AvatarFallback {
    +AvatarFallback
  }

  class Progress {
    +value number
  }

  class Skeleton {
    +Skeleton
  }

  class React {
  }

  class RadixSlot {
  }

  class RadixAvatarRoot {
  }

  class RadixAvatarImage {
  }

  class RadixAvatarFallback {
  }

  class RadixProgressRoot {
  }

  class RadixProgressIndicator {
  }

  class clsx {
  }

  class twMerge {
  }

  cn ..> clsx : uses
  cn ..> twMerge : uses

  Button ..> cn : uses
  Button ..> buttonVariants : uses
  Button ..> React : forwardRef
  Button ..> RadixSlot : uses

  buttonVariants ..> React : cva_variants

  Card ..> cn : uses
  Card ..> React : forwardRef

  CardHeader ..> cn : uses
  CardHeader ..> React : forwardRef

  CardTitle ..> cn : uses
  CardTitle ..> React : forwardRef

  CardDescription ..> cn : uses
  CardDescription ..> React : forwardRef

  CardContent ..> cn : uses
  CardContent ..> React : forwardRef

  CardFooter ..> cn : uses
  CardFooter ..> React : forwardRef

  Avatar ..> cn : uses
  Avatar ..> React : forwardRef
  Avatar ..> RadixAvatarRoot : wraps

  AvatarImage ..> cn : uses
  AvatarImage ..> React : forwardRef
  AvatarImage ..> RadixAvatarImage : wraps

  AvatarFallback ..> cn : uses
  AvatarFallback ..> React : forwardRef
  AvatarFallback ..> RadixAvatarFallback : wraps

  Progress ..> cn : uses
  Progress ..> React : forwardRef
  Progress ..> RadixProgressRoot : wraps
  Progress ..> RadixProgressIndicator : wraps

  Skeleton ..> cn : uses
Loading

File-Level Changes

Change Details Files
Introduce shadcn-style button, card, skeleton, avatar, and progress UI primitives under apps/web/src/new-component/ui/.
  • Add Button component using class-variance-authority variants, Radix Slot for asChild, and shared cn utility for styling.
  • Add Card component with header/title/description/content/footer subcomponents wired to design tokens.
  • Add Avatar component built on @radix-ui/react-avatar with image and fallback variants and client-side directive.
  • Add Progress component built on @radix-ui/react-progress with translateX-based indicator and client-side directive.
  • Add Skeleton component as a simple animated muted-background placeholder.
apps/web/src/new-component/ui/button.tsx
apps/web/src/new-component/ui/card.tsx
apps/web/src/new-component/ui/skeleton.tsx
apps/web/src/new-component/ui/avatar.tsx
apps/web/src/new-component/ui/progress.tsx
Add a shared Tailwind-aware class name utility and configure Tailwind theme tokens to support the new components, including dark-mode variables and animation plugin.
  • Create cn utility that composes clsx and tailwind-merge to resolve Tailwind class conflicts.
  • Define CSS custom properties for color system and radius in :root and .dark using Tailwind base layer.
  • Enable class-based dark mode and extend Tailwind theme with color keys mapped to the CSS variables and radius scale.
  • Register tailwindcss-animate plugin alongside existing daisyUI plugin.
apps/web/src/lib/utils.ts
apps/web/app/globals.css
apps/web/tailwind.config.js
Add Radix UI and styling-related dependencies needed by the new components.
  • Add Radix Slot, Avatar, and Progress packages for composition and accessible primitives.
  • Add class-variance-authority, clsx, tailwind-merge, and tailwindcss-animate for styling utilities and animations.
  • Add lucide-react dependency presumably for future icon usage in UI components.
  • Update package-lock.json to lock new dependencies.
package.json
package-lock.json
Prepare shadcn configuration metadata for the web app.
  • Introduce apps/web/components.json placeholder file for shadcn configuration or future component metadata.
apps/web/components.json

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@nx-cloud

nx-cloud Bot commented Apr 13, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit 75ff0cb

Command Status Duration Result
nx build web ✅ Succeeded 1s View ↗

☁️ Nx Cloud last updated this comment at 2026-04-13 08:42:47 UTC

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • In button.tsx, className is being passed into buttonVariants as a variant (buttonVariants({ variant, size, className })); to avoid unintended behavior and keep className purely for style overrides, call cn(buttonVariants({ variant, size }), className) instead.
  • In card.tsx, the forwardRef generics and element types are inconsistent for CardTitle and CardDescription (e.g., HTMLDivElement with HTMLAttributes<HTMLHeadingElement> but rendering a <div>); align the generic element types, props, and rendered tags (e.g., use <h3>/HTMLHeadingElement and <p>/HTMLParagraphElement) to avoid type mismatch and improve semantics.
  • In skeleton.tsx, React.HTMLAttributes<HTMLDivElement> is used without importing React; add a React import (or switch to the JSX.IntrinsicElements['div'] type) so the file type-checks correctly.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `button.tsx`, `className` is being passed into `buttonVariants` as a variant (`buttonVariants({ variant, size, className })`); to avoid unintended behavior and keep `className` purely for style overrides, call `cn(buttonVariants({ variant, size }), className)` instead.
- In `card.tsx`, the `forwardRef` generics and element types are inconsistent for `CardTitle` and `CardDescription` (e.g., `HTMLDivElement` with `HTMLAttributes<HTMLHeadingElement>` but rendering a `<div>`); align the generic element types, props, and rendered tags (e.g., use `<h3>`/`HTMLHeadingElement` and `<p>`/`HTMLParagraphElement`) to avoid type mismatch and improve semantics.
- In `skeleton.tsx`, `React.HTMLAttributes<HTMLDivElement>` is used without importing React; add a `React` import (or switch to the `JSX.IntrinsicElements['div']` type) so the file type-checks correctly.

## Individual Comments

### Comment 1
<location path="apps/web/src/new-component/ui/card.tsx" line_range="32-8" />
<code_context>
+));
+CardHeader.displayName = 'CardHeader';
+
+const CardTitle = React.forwardRef<
+  HTMLDivElement,
+  React.HTMLAttributes<HTMLHeadingElement>
+>(({ className, ...props }, ref) => (
+  <div
+    ref={ref}
</code_context>
<issue_to_address>
**issue:** Align CardTitle element type with its props typing for better semantics and type safety.

CardTitle’s props are typed as `React.HTMLAttributes<HTMLHeadingElement>` but it renders a `<div>` and uses `HTMLDivElement` as the ref. This inconsistency can cause misleading typing (e.g., heading-only attributes) and less semantic HTML. Please either render a heading element (`<h2>`/`<h3>`) and update the ref accordingly, or change the props to `React.HTMLAttributes<HTMLDivElement>` so the element and types match.
</issue_to_address>

### Comment 2
<location path="apps/web/src/new-component/ui/card.tsx" line_range="47-8" />
<code_context>
+));
+CardTitle.displayName = 'CardTitle';
+
+const CardDescription = React.forwardRef<
+  HTMLDivElement,
+  React.HTMLAttributes<HTMLParagraphElement>
+>(({ className, ...props }, ref) => (
+  <div
+    ref={ref}
</code_context>
<issue_to_address>
**issue:** Fix CardDescription props/ref typing to match the rendered element.

This renders a `<div>` but uses `React.HTMLAttributes<HTMLParagraphElement>` while the ref is `HTMLDivElement`, which is inconsistent. Please either switch the rendered element to `<p>` and update the ref type, or change the props type to use `HTMLDivElement` so everything matches.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

const Card = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: Align CardTitle element type with its props typing for better semantics and type safety.

CardTitle’s props are typed as React.HTMLAttributes<HTMLHeadingElement> but it renders a <div> and uses HTMLDivElement as the ref. This inconsistency can cause misleading typing (e.g., heading-only attributes) and less semantic HTML. Please either render a heading element (<h2>/<h3>) and update the ref accordingly, or change the props to React.HTMLAttributes<HTMLDivElement> so the element and types match.

const Card = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: Fix CardDescription props/ref typing to match the rendered element.

This renders a <div> but uses React.HTMLAttributes<HTMLParagraphElement> while the ref is HTMLDivElement, which is inconsistent. Please either switch the rendered element to <p> and update the ref type, or change the props type to use HTMLDivElement so everything matches.

@fray-cloud

Copy link
Copy Markdown
Owner Author

Superseded by #61 — full #48#54 chain lands via #61.

@fray-cloud fray-cloud closed this Apr 13, 2026
@fray-cloud fray-cloud deleted the shadcn/49-components branch April 13, 2026 14:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant