[CLAUDE] FE: content polish — typography + PageHeader + Button/Input/Table
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m54s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m54s
Typography base (both apps): - 14px base, line-height 1.55, letter-spacing -0.003em — Be Vietnam Pro render chắc hơn, dấu tiếng Việt rõ hơn - h1/h2 tracking tighter (-0.018em), h1 weight 700, h2 weight 600 - Tabular numbers trong <table> (tự động align cột số) PageHeader: - Title text-[22px] thay vì text-xl — hierarchy mạnh hơn - Border-bottom + pb-5 thay flat layout — content-area rõ vùng - Description leading-relaxed + slate-500 — dễ đọc hơn Button: - shadow-sm + color-tinted shadow (brand/20, red/20) cho primary/ danger — có chiều sâu - active:translate-y-[0.5px] micro-press feedback - Ring offset 2 (thay 1) + offset-white — focus ring tách rõ Input/Select/Textarea: - h-9 thay h-10 — phù hợp dense table layouts - shadow-[inset_0_1px_0_...] — inset highlight tinh tế - Focus: border-brand-500 + ring-brand-500/20 — 2 lớp chỉ báo - Disabled: bg-slate-50 + opacity-70 — rõ disabled state DataTable: - rounded-xl + shadow-sm + border-200/80 — card feel nhẹ nhàng hơn - Header: UPPERCASE text-[11px] tracking-wider — ERP enterprise look - Row hover: bg-brand-50/40 (thay slate-50) — brand-tinted hover - Padding tăng từ px-3 py-2 → px-4 py-2.5 — breathing room Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -35,15 +35,15 @@ export function DataTable<T>({
|
|||||||
onRowClick,
|
onRowClick,
|
||||||
}: Props<T>) {
|
}: Props<T>) {
|
||||||
return (
|
return (
|
||||||
<div className="overflow-auto rounded-md border border-slate-200 bg-white">
|
<div className="overflow-auto rounded-xl border border-slate-200/80 bg-white shadow-sm">
|
||||||
<table className="w-full text-sm">
|
<table className="w-full text-sm">
|
||||||
<thead className="bg-slate-50 text-slate-700">
|
<thead className="bg-slate-50/60 text-slate-600">
|
||||||
<tr>
|
<tr className="border-b border-slate-200/80">
|
||||||
{columns.map(c => (
|
{columns.map(c => (
|
||||||
<th
|
<th
|
||||||
key={c.key}
|
key={c.key}
|
||||||
className={cn(
|
className={cn(
|
||||||
'px-3 py-2 font-medium',
|
'px-4 py-2.5 text-[11px] font-semibold uppercase tracking-wider',
|
||||||
c.align === 'right' && 'text-right',
|
c.align === 'right' && 'text-right',
|
||||||
c.align === 'center' && 'text-center',
|
c.align === 'center' && 'text-center',
|
||||||
c.align !== 'right' && c.align !== 'center' && 'text-left',
|
c.align !== 'right' && c.align !== 'center' && 'text-left',
|
||||||
@ -70,7 +70,7 @@ export function DataTable<T>({
|
|||||||
Array.from({ length: 5 }).map((_, i) => (
|
Array.from({ length: 5 }).map((_, i) => (
|
||||||
<tr key={`sk-${i}`} className="border-t border-slate-100">
|
<tr key={`sk-${i}`} className="border-t border-slate-100">
|
||||||
{columns.map(c => (
|
{columns.map(c => (
|
||||||
<td key={c.key} className="px-3 py-3">
|
<td key={c.key} className="px-4 py-3">
|
||||||
<div className="h-3 animate-pulse rounded bg-slate-100" />
|
<div className="h-3 animate-pulse rounded bg-slate-100" />
|
||||||
</td>
|
</td>
|
||||||
))}
|
))}
|
||||||
@ -78,7 +78,7 @@ export function DataTable<T>({
|
|||||||
))}
|
))}
|
||||||
{!isLoading && rows.length === 0 && (
|
{!isLoading && rows.length === 0 && (
|
||||||
<tr>
|
<tr>
|
||||||
<td colSpan={columns.length} className="px-3 py-10 text-center text-sm text-slate-400">
|
<td colSpan={columns.length} className="px-4 py-12 text-center text-sm text-slate-400">
|
||||||
{empty ?? 'Không có dữ liệu'}
|
{empty ?? 'Không có dữ liệu'}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -88,8 +88,8 @@ export function DataTable<T>({
|
|||||||
<tr
|
<tr
|
||||||
key={getRowKey(row)}
|
key={getRowKey(row)}
|
||||||
className={cn(
|
className={cn(
|
||||||
'border-t border-slate-100 transition',
|
'border-t border-slate-100 text-slate-700 transition-colors',
|
||||||
onRowClick && 'cursor-pointer hover:bg-slate-50',
|
onRowClick && 'cursor-pointer hover:bg-brand-50/40',
|
||||||
)}
|
)}
|
||||||
onClick={() => onRowClick?.(row)}
|
onClick={() => onRowClick?.(row)}
|
||||||
>
|
>
|
||||||
@ -97,7 +97,7 @@ export function DataTable<T>({
|
|||||||
<td
|
<td
|
||||||
key={c.key}
|
key={c.key}
|
||||||
className={cn(
|
className={cn(
|
||||||
'px-3 py-2',
|
'px-4 py-2.5',
|
||||||
c.align === 'right' && 'text-right',
|
c.align === 'right' && 'text-right',
|
||||||
c.align === 'center' && 'text-center',
|
c.align === 'center' && 'text-center',
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -1,13 +1,21 @@
|
|||||||
import type { ReactNode } from 'react'
|
import type { ReactNode } from 'react'
|
||||||
|
|
||||||
export function PageHeader({ title, description, actions }: { title: ReactNode; description?: ReactNode; actions?: ReactNode }) {
|
export function PageHeader({
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
actions,
|
||||||
|
}: {
|
||||||
|
title: ReactNode
|
||||||
|
description?: ReactNode
|
||||||
|
actions?: ReactNode
|
||||||
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="mb-5 flex items-start justify-between gap-4">
|
<div className="mb-6 flex items-start justify-between gap-6 border-b border-slate-200/70 pb-5">
|
||||||
<div>
|
<div className="min-w-0">
|
||||||
<h1 className="text-xl font-bold text-slate-900">{title}</h1>
|
<h1 className="text-[22px] font-bold leading-tight text-slate-900">{title}</h1>
|
||||||
{description && <p className="mt-1 text-sm text-slate-600">{description}</p>}
|
{description && <p className="mt-1.5 text-[13px] leading-relaxed text-slate-500">{description}</p>}
|
||||||
</div>
|
</div>
|
||||||
{actions && <div className="flex items-center gap-2">{actions}</div>}
|
{actions && <div className="flex shrink-0 items-center gap-2">{actions}</div>}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,19 +3,19 @@ import { cva, type VariantProps } from 'class-variance-authority'
|
|||||||
import { cn } from '@/lib/cn'
|
import { cn } from '@/lib/cn'
|
||||||
|
|
||||||
const buttonVariants = cva(
|
const buttonVariants = cva(
|
||||||
'inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium transition disabled:pointer-events-none disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-brand-500',
|
'inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium transition-colors disabled:pointer-events-none disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-offset-white focus-visible:ring-brand-500 active:translate-y-[0.5px]',
|
||||||
{
|
{
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
primary: 'bg-brand-600 text-white hover:bg-brand-700',
|
primary: 'bg-brand-600 text-white shadow-sm shadow-brand-600/20 hover:bg-brand-700',
|
||||||
secondary: 'bg-slate-200 text-slate-900 hover:bg-slate-300',
|
secondary: 'bg-slate-100 text-slate-800 hover:bg-slate-200',
|
||||||
outline: 'border border-slate-300 bg-white hover:bg-slate-50',
|
outline: 'border border-slate-300 bg-white text-slate-700 shadow-sm hover:bg-slate-50 hover:border-slate-400',
|
||||||
ghost: 'hover:bg-slate-100',
|
ghost: 'text-slate-600 hover:bg-slate-100 hover:text-slate-900',
|
||||||
danger: 'bg-red-600 text-white hover:bg-red-700',
|
danger: 'bg-red-600 text-white shadow-sm shadow-red-600/20 hover:bg-red-700',
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
sm: 'h-8 px-3',
|
sm: 'h-8 px-3 text-xs',
|
||||||
md: 'h-10 px-4',
|
md: 'h-9 px-4',
|
||||||
lg: 'h-11 px-6 text-base',
|
lg: 'h-11 px-6 text-base',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -7,7 +7,10 @@ export const Input = forwardRef<HTMLInputElement, Props>(({ className, ...props
|
|||||||
<input
|
<input
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'h-10 w-full rounded-md border border-slate-300 bg-white px-3 text-sm placeholder:text-slate-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 disabled:opacity-50',
|
'h-9 w-full rounded-md border border-slate-300 bg-white px-3 text-sm text-slate-900',
|
||||||
|
'shadow-[inset_0_1px_0_rgba(15,23,42,0.02)] placeholder:text-slate-400',
|
||||||
|
'transition-[border-color,box-shadow] focus-visible:border-brand-500 focus-visible:ring-2 focus-visible:ring-brand-500/20',
|
||||||
|
'disabled:cursor-not-allowed disabled:bg-slate-50 disabled:opacity-70',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@ -7,7 +7,10 @@ export const Select = forwardRef<HTMLSelectElement, Props>(({ className, childre
|
|||||||
<select
|
<select
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'h-10 w-full rounded-md border border-slate-300 bg-white px-3 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 disabled:opacity-50',
|
'h-9 w-full rounded-md border border-slate-300 bg-white px-3 pr-8 text-sm text-slate-900',
|
||||||
|
'shadow-[inset_0_1px_0_rgba(15,23,42,0.02)]',
|
||||||
|
'transition-[border-color,box-shadow] focus-visible:border-brand-500 focus-visible:ring-2 focus-visible:ring-brand-500/20',
|
||||||
|
'disabled:cursor-not-allowed disabled:bg-slate-50 disabled:opacity-70',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@ -7,7 +7,10 @@ export const Textarea = forwardRef<HTMLTextAreaElement, Props>(({ className, ...
|
|||||||
<textarea
|
<textarea
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'w-full rounded-md border border-slate-300 bg-white px-3 py-2 text-sm placeholder:text-slate-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 disabled:opacity-50',
|
'w-full rounded-md border border-slate-300 bg-white px-3 py-2 text-sm text-slate-900 leading-relaxed',
|
||||||
|
'shadow-[inset_0_1px_0_rgba(15,23,42,0.02)] placeholder:text-slate-400',
|
||||||
|
'transition-[border-color,box-shadow] focus-visible:border-brand-500 focus-visible:ring-2 focus-visible:ring-brand-500/20',
|
||||||
|
'disabled:cursor-not-allowed disabled:bg-slate-50 disabled:opacity-70',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@ -32,12 +32,32 @@ body {
|
|||||||
background-color: #f8fafc;
|
background-color: #f8fafc;
|
||||||
color: #0f172a;
|
color: #0f172a;
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
font-feature-settings: "cv11", "ss01", "ss03";
|
font-size: 14px;
|
||||||
|
line-height: 1.55;
|
||||||
|
letter-spacing: -0.003em;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
text-rendering: optimizeLegibility;
|
text-rendering: optimizeLegibility;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Heading tightening + better hierarchy with Be Vietnam Pro */
|
||||||
|
h1, h2, h3, h4 {
|
||||||
|
letter-spacing: -0.018em;
|
||||||
|
color: #0f172a;
|
||||||
|
}
|
||||||
|
h1 { font-weight: 700; }
|
||||||
|
h2 { font-weight: 600; }
|
||||||
|
|
||||||
|
/* Tabular numbers in tables + stat cards for better alignment */
|
||||||
|
table, .tabular-nums {
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Smoother form focus */
|
||||||
|
input:focus-visible, textarea:focus-visible, select:focus-visible, button:focus-visible {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* Subtle scrollbar that fits the brand */
|
/* Subtle scrollbar that fits the brand */
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 8px;
|
width: 8px;
|
||||||
|
|||||||
@ -35,15 +35,15 @@ export function DataTable<T>({
|
|||||||
onRowClick,
|
onRowClick,
|
||||||
}: Props<T>) {
|
}: Props<T>) {
|
||||||
return (
|
return (
|
||||||
<div className="overflow-auto rounded-md border border-slate-200 bg-white">
|
<div className="overflow-auto rounded-xl border border-slate-200/80 bg-white shadow-sm">
|
||||||
<table className="w-full text-sm">
|
<table className="w-full text-sm">
|
||||||
<thead className="bg-slate-50 text-slate-700">
|
<thead className="bg-slate-50/60 text-slate-600">
|
||||||
<tr>
|
<tr className="border-b border-slate-200/80">
|
||||||
{columns.map(c => (
|
{columns.map(c => (
|
||||||
<th
|
<th
|
||||||
key={c.key}
|
key={c.key}
|
||||||
className={cn(
|
className={cn(
|
||||||
'px-3 py-2 font-medium',
|
'px-4 py-2.5 text-[11px] font-semibold uppercase tracking-wider',
|
||||||
c.align === 'right' && 'text-right',
|
c.align === 'right' && 'text-right',
|
||||||
c.align === 'center' && 'text-center',
|
c.align === 'center' && 'text-center',
|
||||||
c.align !== 'right' && c.align !== 'center' && 'text-left',
|
c.align !== 'right' && c.align !== 'center' && 'text-left',
|
||||||
@ -70,7 +70,7 @@ export function DataTable<T>({
|
|||||||
Array.from({ length: 5 }).map((_, i) => (
|
Array.from({ length: 5 }).map((_, i) => (
|
||||||
<tr key={`sk-${i}`} className="border-t border-slate-100">
|
<tr key={`sk-${i}`} className="border-t border-slate-100">
|
||||||
{columns.map(c => (
|
{columns.map(c => (
|
||||||
<td key={c.key} className="px-3 py-3">
|
<td key={c.key} className="px-4 py-3">
|
||||||
<div className="h-3 animate-pulse rounded bg-slate-100" />
|
<div className="h-3 animate-pulse rounded bg-slate-100" />
|
||||||
</td>
|
</td>
|
||||||
))}
|
))}
|
||||||
@ -78,7 +78,7 @@ export function DataTable<T>({
|
|||||||
))}
|
))}
|
||||||
{!isLoading && rows.length === 0 && (
|
{!isLoading && rows.length === 0 && (
|
||||||
<tr>
|
<tr>
|
||||||
<td colSpan={columns.length} className="px-3 py-10 text-center text-sm text-slate-400">
|
<td colSpan={columns.length} className="px-4 py-12 text-center text-sm text-slate-400">
|
||||||
{empty ?? 'Không có dữ liệu'}
|
{empty ?? 'Không có dữ liệu'}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -88,8 +88,8 @@ export function DataTable<T>({
|
|||||||
<tr
|
<tr
|
||||||
key={getRowKey(row)}
|
key={getRowKey(row)}
|
||||||
className={cn(
|
className={cn(
|
||||||
'border-t border-slate-100 transition',
|
'border-t border-slate-100 text-slate-700 transition-colors',
|
||||||
onRowClick && 'cursor-pointer hover:bg-slate-50',
|
onRowClick && 'cursor-pointer hover:bg-brand-50/40',
|
||||||
)}
|
)}
|
||||||
onClick={() => onRowClick?.(row)}
|
onClick={() => onRowClick?.(row)}
|
||||||
>
|
>
|
||||||
@ -97,7 +97,7 @@ export function DataTable<T>({
|
|||||||
<td
|
<td
|
||||||
key={c.key}
|
key={c.key}
|
||||||
className={cn(
|
className={cn(
|
||||||
'px-3 py-2',
|
'px-4 py-2.5',
|
||||||
c.align === 'right' && 'text-right',
|
c.align === 'right' && 'text-right',
|
||||||
c.align === 'center' && 'text-center',
|
c.align === 'center' && 'text-center',
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -1,13 +1,21 @@
|
|||||||
import type { ReactNode } from 'react'
|
import type { ReactNode } from 'react'
|
||||||
|
|
||||||
export function PageHeader({ title, description, actions }: { title: ReactNode; description?: ReactNode; actions?: ReactNode }) {
|
export function PageHeader({
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
actions,
|
||||||
|
}: {
|
||||||
|
title: ReactNode
|
||||||
|
description?: ReactNode
|
||||||
|
actions?: ReactNode
|
||||||
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="mb-5 flex items-start justify-between gap-4">
|
<div className="mb-6 flex items-start justify-between gap-6 border-b border-slate-200/70 pb-5">
|
||||||
<div>
|
<div className="min-w-0">
|
||||||
<h1 className="text-xl font-bold text-slate-900">{title}</h1>
|
<h1 className="text-[22px] font-bold leading-tight text-slate-900">{title}</h1>
|
||||||
{description && <p className="mt-1 text-sm text-slate-600">{description}</p>}
|
{description && <p className="mt-1.5 text-[13px] leading-relaxed text-slate-500">{description}</p>}
|
||||||
</div>
|
</div>
|
||||||
{actions && <div className="flex items-center gap-2">{actions}</div>}
|
{actions && <div className="flex shrink-0 items-center gap-2">{actions}</div>}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,19 +3,19 @@ import { cva, type VariantProps } from 'class-variance-authority'
|
|||||||
import { cn } from '@/lib/cn'
|
import { cn } from '@/lib/cn'
|
||||||
|
|
||||||
const buttonVariants = cva(
|
const buttonVariants = cva(
|
||||||
'inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium transition disabled:pointer-events-none disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-brand-500',
|
'inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium transition-colors disabled:pointer-events-none disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-offset-white focus-visible:ring-brand-500 active:translate-y-[0.5px]',
|
||||||
{
|
{
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
primary: 'bg-brand-600 text-white hover:bg-brand-700',
|
primary: 'bg-brand-600 text-white shadow-sm shadow-brand-600/20 hover:bg-brand-700',
|
||||||
secondary: 'bg-slate-200 text-slate-900 hover:bg-slate-300',
|
secondary: 'bg-slate-100 text-slate-800 hover:bg-slate-200',
|
||||||
outline: 'border border-slate-300 bg-white hover:bg-slate-50',
|
outline: 'border border-slate-300 bg-white text-slate-700 shadow-sm hover:bg-slate-50 hover:border-slate-400',
|
||||||
ghost: 'hover:bg-slate-100',
|
ghost: 'text-slate-600 hover:bg-slate-100 hover:text-slate-900',
|
||||||
danger: 'bg-red-600 text-white hover:bg-red-700',
|
danger: 'bg-red-600 text-white shadow-sm shadow-red-600/20 hover:bg-red-700',
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
sm: 'h-8 px-3',
|
sm: 'h-8 px-3 text-xs',
|
||||||
md: 'h-10 px-4',
|
md: 'h-9 px-4',
|
||||||
lg: 'h-11 px-6 text-base',
|
lg: 'h-11 px-6 text-base',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -7,7 +7,10 @@ export const Input = forwardRef<HTMLInputElement, Props>(({ className, ...props
|
|||||||
<input
|
<input
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'h-10 w-full rounded-md border border-slate-300 bg-white px-3 text-sm placeholder:text-slate-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 disabled:opacity-50',
|
'h-9 w-full rounded-md border border-slate-300 bg-white px-3 text-sm text-slate-900',
|
||||||
|
'shadow-[inset_0_1px_0_rgba(15,23,42,0.02)] placeholder:text-slate-400',
|
||||||
|
'transition-[border-color,box-shadow] focus-visible:border-brand-500 focus-visible:ring-2 focus-visible:ring-brand-500/20',
|
||||||
|
'disabled:cursor-not-allowed disabled:bg-slate-50 disabled:opacity-70',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@ -7,7 +7,10 @@ export const Select = forwardRef<HTMLSelectElement, Props>(({ className, childre
|
|||||||
<select
|
<select
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'h-10 w-full rounded-md border border-slate-300 bg-white px-3 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 disabled:opacity-50',
|
'h-9 w-full rounded-md border border-slate-300 bg-white px-3 pr-8 text-sm text-slate-900',
|
||||||
|
'shadow-[inset_0_1px_0_rgba(15,23,42,0.02)]',
|
||||||
|
'transition-[border-color,box-shadow] focus-visible:border-brand-500 focus-visible:ring-2 focus-visible:ring-brand-500/20',
|
||||||
|
'disabled:cursor-not-allowed disabled:bg-slate-50 disabled:opacity-70',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@ -7,7 +7,10 @@ export const Textarea = forwardRef<HTMLTextAreaElement, Props>(({ className, ...
|
|||||||
<textarea
|
<textarea
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'w-full rounded-md border border-slate-300 bg-white px-3 py-2 text-sm placeholder:text-slate-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 disabled:opacity-50',
|
'w-full rounded-md border border-slate-300 bg-white px-3 py-2 text-sm text-slate-900 leading-relaxed',
|
||||||
|
'shadow-[inset_0_1px_0_rgba(15,23,42,0.02)] placeholder:text-slate-400',
|
||||||
|
'transition-[border-color,box-shadow] focus-visible:border-brand-500 focus-visible:ring-2 focus-visible:ring-brand-500/20',
|
||||||
|
'disabled:cursor-not-allowed disabled:bg-slate-50 disabled:opacity-70',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@ -32,12 +32,29 @@ body {
|
|||||||
background-color: #f8fafc;
|
background-color: #f8fafc;
|
||||||
color: #0f172a;
|
color: #0f172a;
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
font-feature-settings: "cv11", "ss01", "ss03";
|
font-size: 14px;
|
||||||
|
line-height: 1.55;
|
||||||
|
letter-spacing: -0.003em;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
text-rendering: optimizeLegibility;
|
text-rendering: optimizeLegibility;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4 {
|
||||||
|
letter-spacing: -0.018em;
|
||||||
|
color: #0f172a;
|
||||||
|
}
|
||||||
|
h1 { font-weight: 700; }
|
||||||
|
h2 { font-weight: 600; }
|
||||||
|
|
||||||
|
table, .tabular-nums {
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus-visible, textarea:focus-visible, select:focus-visible, button:focus-visible {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 8px;
|
width: 8px;
|
||||||
height: 8px;
|
height: 8px;
|
||||||
|
|||||||
Reference in New Issue
Block a user