Skip to content

Conversation

@kamja44
Copy link

@kamja44 kamja44 commented Dec 23, 2025

Overview

This PR adds a new Timeline component to shadcn/ui v4, addressing long-standing community requests for a timeline component.

Related Issues & Discussions

This PR resolves/addresses:

  • Discussion #4285 - Component Request: Timeline (211 👍)
  • Issue #949 - Component Request: Timeline (173 👍)
  • Issue #2141 - TimeLine component
  • Issue #3584 - [feat]: Timeline Component
  • Issue #6993 - [feat]: Add a timeline component

Features

The Timeline component provides:

  • Multiple layout positions: Left (default), Right, and Alternate positioning
  • Flexible marker variants: Default, Primary, Success, Warning, and Destructive
  • Icon support: Custom icons can be embedded within markers
  • Composite architecture: Modular sub-components (Timeline, TimelineItem, TimelineMarker, TimelineContent, TimelineTitle, TimelineDescription, TimelineTime)
  • Full accessibility: Semantic HTML with proper ARIA attributes via data-slot
  • Theme integration: Uses OKLCH color system with CSS variables
  • Responsive design: Works seamlessly across all screen sizes
  • TypeScript support: Fully typed with VariantProps

Component Structure

<Timeline position="left">
  <TimelineItem>
    <TimelineMarker variant="primary" icon={<CheckIcon />} />
    <TimelineContent>
      <TimelineTitle>Event Title</TimelineTitle>
      <TimelineDescription>Event description</TimelineDescription>
      <TimelineTime>January 15, 2024 at 9:00 AM</TimelineTime>
    </TimelineContent>
  </TimelineItem>
</Timeline>

Added Files

UI Component

  • apps/v4/registry/new-york-v4/ui/timeline.tsx
  • apps/v4/registry/new-york-v4/ui/_registry.ts (updated)

Examples

  • apps/v4/registry/new-york-v4/examples/timeline-demo.tsx
  • apps/v4/registry/new-york-v4/examples/timeline-with-icons.tsx
  • apps/v4/registry/new-york-v4/examples/timeline-right.tsx
  • apps/v4/registry/new-york-v4/examples/timeline-alternate.tsx (or timeline-center.tsx)
  • apps/v4/registry/new-york-v4/examples/_registry.ts (updated)

Documentation

  • apps/v4/content/docs/components/timeline.mdx

Screenshots

Basic Timeline (Left Position)

image ### Timeline with Icons image ### Right Position Timeline image ### Alternate Position Timeline image ### Center Position Timeline image

Implementation Details

  • Uses class-variance-authority (CVA) for variant management
  • Implements React Context for position state management
  • Follows shadcn/ui v4 patterns: data-slot attributes, OKLCH colors, forwardRef
  • Includes "use client" directive for Next.js 13+ compatibility
  • CSS-based connector lines with smart hiding on last item

Checklist

[O] Follows shadcn/ui design patterns and conventions
[O] Uses data-slot attributes for semantic styling
[O] Integrates with OKLCH color system
[O] Includes comprehensive documentation with examples
[O] All examples render correctly
[O] TypeScript types are properly defined
[O] Component is fully accessible
[O] Works with both light and dark themes
[O] Tested in development environment

Testing

Tested locally with:

pnpm registry:build
pnpm --filter=v4 dev
All examples render correctly at http://localhost:4000/docs/components/timeline

- Add Timeline component with multiple layout positions
- Support left, right, and alternate positioning
- Include icon support in markers
- Add comprehensive documentation and examples

Resolves shadcn-ui#949, shadcn-ui#2141, shadcn-ui#3584, shadcn-ui#6993
Addresses discussion shadcn-ui#4285
@vercel
Copy link
Contributor

vercel bot commented Dec 23, 2025

@kamja44 is attempting to deploy a commit to the shadcn-pro Team on Vercel.

A member of the Team first needs to authorize it.

@diffray-bot
Copy link

Changes Summary

This PR adds a new Timeline component to shadcn/ui v4, addressing long-standing community requests. The component provides a modular, composable architecture with multiple layout positions (left, right, alternate, center), marker variants (default, primary, success, warning, destructive), icon support, and full accessibility via semantic HTML and data-slot attributes.

Type: feature

Components Affected: Timeline, TimelineItem, TimelineMarker, TimelineContent, TimelineSpacer, TimelineTitle, TimelineDescription, TimelineTime, Registry

Architecture Impact
  • New Patterns: React Context for position state management, Composite component architecture with sub-components, CVA (class-variance-authority) for variant management
  • Dependencies: added: class-variance-authority (already a project dependency)
  • Coupling: The Timeline component is self-contained with minimal coupling, using only the shared cn utility

Risk Areas: TimelineSpacer has an empty className by default - may need to have actual dimensions for layout to work correctly in center mode, Korean language comments in the generated registry JSON content could be unintentional or cause confusion for contributors, The TimelineItem children rendering assumes valid React elements which may silently skip non-element children, The connector line hiding relies on CSS selector [li:last-child_&]:hidden which may not work correctly if non-li elements are accidentally rendered

Suggestions
  • Consider adding ARIA roles (role='list'/role='listitem') or aria-label for improved screen reader support
  • Add unit tests for the component, especially for the position/variant logic and context usage
  • Consider translating Korean comments in the source code to English for consistency
  • The TimelineSpacer component may benefit from a minimum width/height for consistent layout behavior

Full review in progress... | Powered by diffray

@diffray-bot
Copy link

Review Summary

Free public review - Want AI code reviews on your PRs? Check out diffray.ai

Validated 24 issues: 6 kept (critical bugs and significant quality issues), 18 filtered (low value, autogenerated file context, or inconsistent with project standards)

Issues Found: 6

📊 4 unique issue type(s) across 6 location(s)

📋 Full issue list (click to expand)

🟠 HIGH - Reference to undefined variable 'item' (2 occurrences)

Category: bug

📍 View all locations
File Description Suggestion Confidence
apps/v4/registry/__index__.tsx:21 The code references 'item.name' but 'item' is not defined in the scope. This will cause a ReferenceE... Fix the build script that generates this file to use the correct variable name instead of 'item.name... 95%
apps/v4/registry/__index__.tsx:40 Same bug as line 21 - 'item.name' referenced but 'item' is undefined. This pattern repeats throughou... Same fix needed - update the build script to use the correct variable reference. 95%

Rule: qual_inverted_logic_js


🟡 MEDIUM - Inconsistent import path for cn utility function

Category: quality

File: apps/v4/registry/new-york-v4/ui/timeline.tsx:6

Description: The timeline.tsx component imports the cn utility from @/registry/new-york-v4/lib/utils, but almost all other UI components in the same directory import it from @/lib/utils.

Suggestion: Change line 6 from import { cn } from "@/registry/new-york-v4/lib/utils" to import { cn } from "@/lib/utils" to match the pattern used in 51 out of 53 UI components.

Confidence: 85%

Rule: react_prefer_project_ui_components


🟡 MEDIUM - Timeline marker variants rely solely on color to convey status

Category: quality

File: apps/v4/registry/new-york-v4/ui/timeline.tsx:131-149

Description: The TimelineMarker component uses different color variants (success, warning, destructive, primary) to convey semantic meaning, but color alone is insufficient for accessibility. Users with color blindness cannot distinguish between these states.

Suggestion: Add aria-label to describe the status or include visually hidden text (sr-only) that describes each variant. Example: <span className="sr-only">{variant === 'success' && 'Completed'}</span>

Confidence: 75%

Rule: fe_color_only_indication


🟡 MEDIUM - File-wide type checking disabled with @ts-nocheck (2 occurrences)

Category: quality

📍 View all locations
File Description Suggestion Confidence
apps/v4/registry/__index__.tsx:1 The @ts-nocheck directive disables all TypeScript type checking for the entire file. While this is a... Fix the build script to generate properly typed code. The @ts-nocheck is likely hiding the 'item.nam... 70%
apps/v4/registry/__index__.tsx:6 The type 'Record<string, Record<string, any>>' uses 'any' for nested values, bypassing TypeScript's ... Define a proper interface for registry items in the build script that generates this file, so consum... 65%

Rule: ts_any_type_usage


Powered by diffray - Multi-Agent Code Review Agent

- Add ARIA roles: role="list", role="listitem", role="status"
- Add aria-label with variant-based labels (Event, Important, Completed, Warning, Error)
- Add sr-only text for screen reader support
- Fix import path: @/registry/new-york-v4/lib/utils -> @/lib/utils
- Improve color contrast: text-background -> text-current, bg-background -> bg-current
- Add text-destructive-foreground to destructive variant
- Add TimelineSpacer dimensions: min-w-0 flex-1
- Remove Korean comments and translate to English
@kamja44
Copy link
Author

kamja44 commented Dec 24, 2025

✅ Resolved Issues

1. ARIA roles for improved screen reader support

  • Status: ✅ Fixed
  • Added role="list" to Timeline component (line 60)
  • Added role="listitem" to TimelineItem component (lines 97, 114)
  • Added role="status" to TimelineMarker component (line 186)

2. Timeline marker variants rely solely on color

  • Status: ✅ Fixed
  • Added variantLabels mapping object (lines 155-161)
  • Added aria-label attribute to TimelineMarker (line 185)
  • Added <span className="sr-only"> for screen reader support (line 200)
  • Each variant now has semantic labels: Event, Important, Completed, Warning, Error

3. Inconsistent import path for cn utility

  • Status: ✅ Fixed
  • Changed from @/registry/new-york-v4/lib/utils to @/lib/utils (line 6)
  • Now matches the pattern used by other UI components

4. TimelineSpacer dimensions

  • Status: ✅ Fixed
  • Added min-w-0 flex-1 classes to TimelineSpacer (line 270)
  • Ensures proper layout behavior in center mode

5. Korean language comments

  • Status: ✅ Fixed
  • All comments have been translated to English
  • JSON registry files automatically regenerated with English comments

⚠️ Not Addressed (Out of Scope)

1. Reference to undefined variable 'item' (HIGH)

  • File: apps/v4/registry/__index__.tsx (lines 21, 40)
  • Status: ⚠️ Not fixed - Out of scope
  • Reason: This is a pre-existing bug in the build script (build-registry.mts), not introduced by this PR. The file is auto-generated and should not be manually edited. This issue affects all components in the registry, not just Timeline. Fixing this would require modifying the build script, which is outside the scope of this PR and should be addressed in a separate PR.

2. File-wide type checking disabled with @ts-nocheck

  • File: apps/v4/registry/__index__.tsx (line 1)
  • Status: ⚠️ Not fixed - Out of scope
  • Reason: Same as above - this is a pre-existing issue in the auto-generated file caused by the build script. Should be fixed in a separate PR that addresses the build script issues.

All accessibility and code quality issues directly related to the Timeline component have been addressed. The auto-generated file issues are pre-existing and should be tackled separately.

@diffray-bot
Copy link

Changes Summary

This PR adds a new Timeline component to shadcn/ui v4, implementing a highly-requested feature (combined ~400 upvotes across related issues). The component provides a composable timeline with multiple layout positions (left, right, alternate, center), variant markers with icon support, and proper accessibility via ARIA attributes and semantic HTML.

Type: feature

Components Affected: Timeline (new component), TimelineItem, TimelineMarker, TimelineContent, TimelineSpacer, TimelineTitle, TimelineDescription, TimelineTime, Registry (updated with new component entries)

Architecture Impact
  • New Patterns: React Context for position state propagation (TimelineContext, TimelineItemContext), Composite component pattern with multiple sub-components, CVA (class-variance-authority) for variant management
  • Dependencies: added: class-variance-authority (already in project, declared as component dependency)
  • Coupling: Self-contained component with no coupling to other UI components beyond utility functions (cn)

Risk Areas: Auto-generated registry files (elevenlabs.json and other JSON files) included in the commit may be unintentional side effects of registry:build, TimelineMarker uses role='status' which may need validation for correct semantic usage in timeline context, Center position layout requires manual use of TimelineSpacer for proper alignment - could be error-prone for users

Suggestions
  • Consider whether the unrelated elevenlabs.json files and other registry JSON changes should be excluded from this PR
  • Add unit/integration tests for the Timeline component
  • Consider whether role='status' is the most appropriate ARIA role for timeline markers vs role='presentation' or no role

Full review in progress... | Powered by diffray

Comment on lines +164 to +166

Timeline with a centered vertical line and content alternating on both sides. Perfect for project roadmaps and process flows.

Choose a reason for hiding this comment

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

🟡 MEDIUM - Center Position description missing critical detail
Category: docs

Description:
Documentation states center position has alternating content but doesn't explain that users must manually order TimelineContent, TimelineMarker, and TimelineSpacer components.

Suggestion:
Add clarification: 'In center position, manually arrange TimelineContent, TimelineMarker, and TimelineSpacer to control which side content appears on.'

Confidence: 75%
Rule: ts_jsdoc_capability_claim_false

Copy link
Author

Choose a reason for hiding this comment

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

The documentation now explicitly explains that in center position, users need to manually arrange TimelineContent, TimelineMarker, and TimelineSpacer components to control which side the content appears on:

In center position, manually arrange TimelineContent, TimelineMarker, and TimelineSpacer to control which side content appears on. Place content before the marker for left-side display, or after the marker (with TimelineSpacer first) for right-side display.

This should make it much clearer for users how to use the center position layout.

Comment on lines 178 to 186
{/* Left side content */}
<TimelineItem>
<TimelineContent>
<TimelineTitle>Step 1</TimelineTitle>
<TimelineDescription>Content on the left</TimelineDescription>
</TimelineContent>
<TimelineMarker />
<TimelineSpacer />
</TimelineItem>

Choose a reason for hiding this comment

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

🔵 LOW - Center position example comment could be clearer
Category: docs

Description:
Comment says 'Left side content' but content has right-aligned text (text-right class for even index). Grid position is left but text alignment is right.

Suggestion:
Change comment to '/* Left column, right-aligned text */' or explain the distinction between grid position and text alignment.

Confidence: 65%
Rule: ts_jsdoc_example_wrong

Copy link
Author

Choose a reason for hiding this comment

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

Fixed in the latest commit! I've updated the comments to clarify both the grid position and text alignment direction:

  • {/* Left column - text is right-aligned towards the center line */}
  • {/* Right column - text is left-aligned towards the center line */}

This makes it clear that:

  • The grid position (left/right column)
  • The text alignment (aligned towards the center line)

are two separate aspects of the layout.

@diffray-bot
Copy link

Review Summary

Free public review - Want AI code reviews on your PRs? Check out diffray.ai

Validated 34 issues: 6 kept, 28 filtered

Issues Found: 6

See 2 individual line comment(s) for details.

📋 Full issue list (click to expand)

🔴 CRITICAL - Reference to undefined variable 'item' in closure

Category: bug

File: apps/v4/registry/__index__.tsx:21

Description: The React.lazy callback references an undefined variable 'item' which does not exist in the closure scope. This pattern repeats 437 times throughout the file. The variable 'item' is not defined anywhere, causing ReferenceError at runtime when lazy component loads.

Suggestion: The 'item' variable should be replaced with the actual object that contains the 'name' property. Since this is an auto-generated file (noted in the header), the build script at scripts/build-registry.ts needs to be fixed to generate the correct variable reference, or use a literal string like 'accordion' for each component.

Confidence: 98%

Rule: ts_add_null_checks_before_accessing_propert


🟡 MEDIUM - Duplicate aria-label and sr-only text

Category: quality

File: apps/v4/registry/new-york-v4/ui/timeline.tsx:169-211

Description: TimelineMarker has both aria-label (line 185) and sr-only span (line 201) providing identical text from variantLabels, causing redundant screen reader announcements.

Suggestion: Remove either the aria-label attribute or the sr-only span to avoid duplicate announcements.

Confidence: 85%

Rule: fe_aria_live_regions


🟡 MEDIUM - Center Position description missing critical detail

Category: docs

File: apps/v4/content/docs/components/timeline.mdx:164-166

Description: Documentation states center position has alternating content but doesn't explain that users must manually order TimelineContent, TimelineMarker, and TimelineSpacer components.

Suggestion: Add clarification: 'In center position, manually arrange TimelineContent, TimelineMarker, and TimelineSpacer to control which side content appears on.'

Confidence: 75%

Rule: ts_jsdoc_capability_claim_false


🔵 LOW - Inconsistent null coalescing operators

Category: quality

File: apps/v4/registry/new-york-v4/ui/timeline.tsx:172-201

Description: Line 172 uses 'variant || "default"' while line 201 uses 'variant ?? "default"'. Minor inconsistency - both work correctly since variant is typed and empty string is not a valid value.

Suggestion: Use '??' consistently for proper null/undefined checking: 'variantLabels[variant ?? "default"]'

Confidence: 65%

Rule: bug_null_pointer


🔵 LOW - Center position example comment could be clearer

Category: docs

File: apps/v4/content/docs/components/timeline.mdx:178-186

Description: Comment says 'Left side content' but content has right-aligned text (text-right class for even index). Grid position is left but text alignment is right.

Suggestion: Change comment to '/* Left column, right-aligned text */' or explain the distinction between grid position and text alignment.

Confidence: 65%

Rule: ts_jsdoc_example_wrong


🔵 LOW - React.Children.map returns unmodified non-elements

Category: quality

File: apps/v4/registry/new-york-v4/ui/timeline.tsx:45-54

Description: Non-element children (null, undefined, strings) pass through without TimelineItemContext.Provider wrapping, which is intentional but could be documented.

Suggestion: Add a comment explaining that non-element children are passed through intentionally, or use React.Children.toArray() with filtering for clearer intent.

Confidence: 60%

Rule: ts_type_assertion_abuse


Powered by diffray - Multi-Agent Code Review Agent

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.

2 participants