Files
goodgo-platform/apps/web/components/design-system/density-provider.tsx
Ho Ngoc Hai 7d6fcb4d8d feat(web): design tokens, Tailwind config, base components (TEC-3057)
- Add chart palette, motion, and z-index CSS vars to globals.css
- Replace custom theme-provider with next-themes (dark default)
- Extend tailwind.config.ts with heading fonts, spacing (row-compact,
  row-roomy, sidebar), chart colors, elevation shadows, glow shadows,
  transition timing, pill border-radius, z-index scale
- Update tick-flash animations to match design token spec (480ms)
- Add prefers-reduced-motion support for all animations
- Create base design-system components:
  Surface, SurfaceElevated, Divider, DensityProvider/useDensity,
  Numeric (VND/percent/compact formatting), Signal (up/down/neutral pill)
- Add dev-only /dev/tokens showcase route (404 in production)
- Update theme-provider tests to match next-themes integration

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-21 03:19:40 +07:00

71 lines
1.8 KiB
TypeScript

'use client';
import { createContext, useCallback, useContext, useEffect, useState } from 'react';
export type DensityMode = 'compact' | 'regular' | 'roomy';
interface DensityContextValue {
density: DensityMode;
setDensity: (mode: DensityMode) => void;
}
const STORAGE_KEY = 'goodgo.density';
const DensityContext = createContext<DensityContextValue>({
density: 'regular',
setDensity: () => {},
});
export function useDensity() {
return useContext(DensityContext);
}
/** Row height in Tailwind spacing tokens per density mode. */
export const DENSITY_ROW_HEIGHT: Record<DensityMode, string> = {
compact: 'h-row-compact', // 32px
regular: 'h-row', // 36px
roomy: 'h-row-roomy', // 44px
};
/** Cell padding classes per density mode. */
export const DENSITY_CELL_PADDING: Record<DensityMode, string> = {
compact: 'px-2 py-1', // 4px 8px
regular: 'px-2.5 py-1.5', // 6px 10px
roomy: 'px-3 py-2.5', // 10px 12px
};
/** Data font size per density mode. */
export const DENSITY_DATA_FONT: Record<DensityMode, string> = {
compact: 'text-data-sm',
regular: 'text-data-md',
roomy: 'text-data-md',
};
export function DensityProvider({
defaultDensity = 'regular',
children,
}: {
defaultDensity?: DensityMode;
children: React.ReactNode;
}) {
const [density, setDensityState] = useState<DensityMode>(defaultDensity);
useEffect(() => {
const stored = localStorage.getItem(STORAGE_KEY) as DensityMode | null;
if (stored === 'compact' || stored === 'regular' || stored === 'roomy') {
setDensityState(stored);
}
}, []);
const setDensity = useCallback((mode: DensityMode) => {
setDensityState(mode);
localStorage.setItem(STORAGE_KEY, mode);
}, []);
return (
<DensityContext.Provider value={{ density, setDensity }}>
{children}
</DensityContext.Provider>
);
}