feat(01-01): implement palette.ts as single source of truth for category colors
- Export CategoryType union type with 7 category strings - Export CategoryShades interface with light/medium/base fields - Export palette Record with oklch values matching --chart-* CSS tokens - Export headerGradient() returning CSSProperties with linear-gradient - Export overviewHeaderGradient() with multi-stop sky/lavender/green gradient - Export amountColorClass() for text-success/text-warning/text-destructive - All 20 unit tests pass
This commit is contained in:
102
frontend/src/lib/palette.ts
Normal file
102
frontend/src/lib/palette.ts
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
import type React from 'react'
|
||||||
|
|
||||||
|
export type CategoryType =
|
||||||
|
| 'income'
|
||||||
|
| 'bill'
|
||||||
|
| 'variable_expense'
|
||||||
|
| 'debt'
|
||||||
|
| 'saving'
|
||||||
|
| 'investment'
|
||||||
|
| 'carryover'
|
||||||
|
|
||||||
|
export interface CategoryShades {
|
||||||
|
light: string
|
||||||
|
medium: string
|
||||||
|
base: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const palette: Record<CategoryType, CategoryShades> = {
|
||||||
|
income: {
|
||||||
|
light: 'oklch(0.96 0.04 145)',
|
||||||
|
medium: 'oklch(0.88 0.08 145)',
|
||||||
|
base: 'oklch(0.76 0.14 145)',
|
||||||
|
},
|
||||||
|
bill: {
|
||||||
|
light: 'oklch(0.96 0.03 250)',
|
||||||
|
medium: 'oklch(0.88 0.07 250)',
|
||||||
|
base: 'oklch(0.76 0.12 250)',
|
||||||
|
},
|
||||||
|
variable_expense: {
|
||||||
|
light: 'oklch(0.97 0.04 85)',
|
||||||
|
medium: 'oklch(0.90 0.08 85)',
|
||||||
|
base: 'oklch(0.80 0.14 85)',
|
||||||
|
},
|
||||||
|
debt: {
|
||||||
|
light: 'oklch(0.96 0.04 15)',
|
||||||
|
medium: 'oklch(0.88 0.08 15)',
|
||||||
|
base: 'oklch(0.76 0.13 15)',
|
||||||
|
},
|
||||||
|
saving: {
|
||||||
|
light: 'oklch(0.95 0.04 280)',
|
||||||
|
medium: 'oklch(0.87 0.08 280)',
|
||||||
|
base: 'oklch(0.75 0.13 280)',
|
||||||
|
},
|
||||||
|
investment: {
|
||||||
|
light: 'oklch(0.96 0.04 320)',
|
||||||
|
medium: 'oklch(0.88 0.07 320)',
|
||||||
|
base: 'oklch(0.76 0.12 320)',
|
||||||
|
},
|
||||||
|
carryover: {
|
||||||
|
light: 'oklch(0.96 0.03 210)',
|
||||||
|
medium: 'oklch(0.88 0.06 210)',
|
||||||
|
base: 'oklch(0.76 0.11 210)',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a CSSProperties object with a linear-gradient background
|
||||||
|
* using the light and medium shades of the given category type.
|
||||||
|
*/
|
||||||
|
export function headerGradient(type: CategoryType): React.CSSProperties {
|
||||||
|
const shades = palette[type]
|
||||||
|
return {
|
||||||
|
background: `linear-gradient(to right, ${shades.light}, ${shades.medium})`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a CSSProperties object with a multi-stop linear-gradient
|
||||||
|
* for the FinancialOverview header (sky via lavender to green).
|
||||||
|
*/
|
||||||
|
export function overviewHeaderGradient(): React.CSSProperties {
|
||||||
|
return {
|
||||||
|
background: `linear-gradient(to right, ${palette.carryover.light}, ${palette.saving.light}, ${palette.income.light})`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AmountColorClassOpts {
|
||||||
|
type: CategoryType
|
||||||
|
actual: number
|
||||||
|
budgeted: number
|
||||||
|
isIncome?: boolean
|
||||||
|
isAvailable?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a Tailwind utility class for coloring a monetary amount:
|
||||||
|
* - isIncome or isAvailable: positive → 'text-success', negative → 'text-destructive', zero → ''
|
||||||
|
* - Expense: actual > budgeted → 'text-warning', else → ''
|
||||||
|
*/
|
||||||
|
export function amountColorClass(opts: AmountColorClassOpts): string {
|
||||||
|
const { actual, budgeted, isIncome, isAvailable } = opts
|
||||||
|
|
||||||
|
if (isIncome || isAvailable) {
|
||||||
|
if (actual > 0) return 'text-success'
|
||||||
|
if (actual < 0) return 'text-destructive'
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expense path
|
||||||
|
if (actual > budgeted) return 'text-warning'
|
||||||
|
return ''
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user