Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions src/components/typography/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import * as React from 'react'
import { Slot } from '@radix-ui/react-slot'
import { cva, type VariantProps } from 'class-variance-authority'
import { cn } from '../../lib/utils'

export const typographyVariants = cva('', {
variants: {
variant: {
h1: 'text-3xl font-semibold text-foreground',
h2: 'text-2xl font-semibold text-foreground',
h3: 'text-xl font-semibold text-foreground',
h4: 'text-lg font-semibold text-foreground',
p: 'leading-7',
blockquote: 'mt-6 border-l-2 pl-6 italic',
lead: 'text-xl text-muted-foreground',
large: 'text-lg font-semibold',
small: 'text-sm font-medium leading-none',
},
textColor: {
muted: 'text-muted-foreground',
},
},
defaultVariants: {
variant: 'p',
},
})

export interface TypographyProps
extends React.HTMLAttributes<HTMLElement>,
VariantProps<typeof typographyVariants> {
asChild?: boolean
}

function defaultTagFor(variant?: TypographyProps['variant']): keyof JSX.IntrinsicElements {
switch (variant) {
case 'h1':
case 'h2':
case 'h3':
case 'h4':
return variant
case 'blockquote':
return 'blockquote'
default:
return 'p'
}
}

export const Typography = React.forwardRef<HTMLElement, TypographyProps>(
({ textColor, asChild = false, variant, className, ...props }, ref) => {
const Comp: React.ElementType = asChild ? Slot : defaultTagFor(variant)
return <Comp ref={ref} className={cn(typographyVariants({ variant, textColor }), className)} {...props} />
}
)
Typography.displayName = 'Typography'
43 changes: 43 additions & 0 deletions src/stories/Typography/Link.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type { Meta, StoryObj } from '@storybook/react'
import { Link } from '../../components/link'

const meta = {
title: 'Typography/Link',
component: Link,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
argTypes: {
variant: {
control: 'select',
options: ['default', 'underline', 'hover'],
description: 'The visual style variant of the link',
},
textColor: {
control: 'select',
options: ['primary', 'inherit'],
description: 'The text color scheme of the link',
defaultValue: 'primary',
},
asChild: {
control: 'boolean',
description: 'When true, renders the child component with Link styles and props merged.',
},
className: {
control: 'text',
description: 'Additional CSS classes for custom styling',
},
},
} satisfies Meta<typeof Link>

export default meta
type Story = StoryObj<typeof meta>

export const Default: Story = {
args: {
children: 'Oasis Explorer',
href: '#',
textColor: 'primary',
},
}
54 changes: 28 additions & 26 deletions src/stories/Typography/Typography.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,45 @@
import type { Meta, StoryObj } from '@storybook/react'
import { Link } from '../../components/link'
import type { Meta, StoryObj } from '@storybook/react-vite'
import { Typography } from '../../components/typography'

const meta = {
title: 'Typography/Link',
component: Link,
const meta: Meta<typeof Typography> = {
title: 'Typography/Typography',
component: Typography,
parameters: {
docs: {
description: {
component:
'Project typography primitives (h1–h4, p, blockquote, small, lead, large). Use the `textColor` prop for muted text.',
},
},
layout: 'centered',
design: {
type: 'figma',
url: 'https://www.figma.com/design/dSsI9L6NSpNCorbSdiYd1k/Oasis-Design-System---shadcn-ui---Default---December-2024?node-id=473-1978',
},
},
tags: ['autodocs'],
argTypes: {
variant: {
control: 'select',
options: ['default', 'underline', 'hover'],
description: 'The visual style variant of the link',
options: ['h1', 'h2', 'h3', 'h4', 'p', 'blockquote', 'lead', 'large', 'small'],
},
textColor: {
control: 'select',
options: ['primary', 'inherit'],
description: 'The text color scheme of the link',
defaultValue: 'primary',
},
asChild: {
control: 'boolean',
description: 'When true, renders the child component with Link styles and props merged.',
},
className: {
control: 'text',
description: 'Additional CSS classes for custom styling',
options: [undefined, 'muted'],
description: 'Optional text color override (e.g., muted).',
},
asChild: { control: 'boolean' },
className: { control: 'text' },
children: { control: 'text' },
},
args: {
variant: 'p',
textColor: undefined,
children: 'The quick brown fox jumps over the lazy dog.',
},
} satisfies Meta<typeof Link>
}

export default meta
type Story = StoryObj<typeof meta>

export const Default: Story = {
args: {
children: 'Oasis Explorer',
href: '#',
textColor: 'primary',
},
}
export const Default: Story = {}