Design System
Theme colors, fonts, and tech stack for the FoodEase ecosystem
Overview
FoodEase uses a cohesive design system across all applications (POS, KDS, and Office) with consistent colors, typography, and styling. This guide covers the complete design system implementation.
Tech Stack
Web Applications (POS & Office)
- Framework: Next.js
- Styling: Tailwind CSS 4+
- UI Components: React + shadcn/ui
Desktop Applications (KDS & POS)
- Framework: Wails (Go/Wails + React)
- Frontend: React + TypeScript
- Styling: Tailwind CSS 3
- Build: Vite
Important Notes
- Tailwind configuration and CSS variable usage may differ between versions dont forget to check the documentation for the specific version you are using and adjust the code accordingly.
Images and Icons
- You can download the wide foodease logo at Download bksp.png
- The square logo can be found at Download bksp.png.
Feel free to use and resize as needed for your application. You can try squoosh for optimizing images for web use.
Typography
Font Families
The FoodEase ecosystem uses two primary fonts:
Jost (Sans-serif)
- Primary font for UI text, buttons, and general content
- Weights: 100-900 (variable)
- Google Fonts: Jost
Cousine (Monospace)
- Used for code, data displays, and technical information
- Weights: 400, 700
- Google Fonts: Cousine
Add these fonts to your project using the next/font/google module in Next.js or by including the appropriate <link> tags in your HTML for non-Next.js projects.
Eg
import type { Metadata } from "next";
import { Jost, Cousine } from "next/font/google";
import "./globals.css";
import "./keyboard.css";
export const metadata: Metadata = {
title: "FoodEase POS",
description: "Generated by create next app",
};
const jost = Jost({
subsets: ["latin"],
weight: ["400", "500", "600", "800", "900"],
display: "swap",
preload: true,
variable: "--jost-font",
});
const cousine = Cousine({
subsets: ["latin"],
weight: ["400", "700"],
display: "swap",
preload: true,
variable: "--cousine-font",
});
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en" suppressHydrationWarning>
<body
suppressHydrationWarning
className={`${jost.variable} ${cousine.variable} antialiased`}
>
{children}
</body>
</html>
);
}Colors
FoodEase uses a custom color system based on CSS variables that makes it easy to maintain consistent theming across your application. Colors are defined using the OKLCH color space for better perceptual uniformity.
CSS Variables (Tailwind 4+)
Tailwind CSS 4 uses CSS variables as the primary method for theming. Add these variables to your global.css or root CSS file (e.g., style.css, app.css, index.css).
Why Tailwind 4?
Tailwind 4 uses @theme inline to automatically map CSS variables to utility classes. This means you can use bg-c1 or text-primary directly instead of the older bg-[var(--c1)] or text-[var(--primary)] syntax used in Tailwind 3.
Version differences:
- Tailwind 3 and earlier: Use bracket notation →
bg-[var(--c1)] - Tailwind 4+: Use direct class names →
bg-c1(requires@theme inlinesetup). You may need to adjust the css below depending on the tailwind version or the framework
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
/* Border Radius */
--radius: 0.625rem;
/* Base Colors */
--background: oklch(0.2122 0.0178 279.8);
--foreground: oklch(1 0 0);
/* Card */
--card: oklch(0.2559 0.0207 280.75);
--card-foreground: oklch(1 0 0);
/* Popover */
--popover: oklch(0.1856 0.0325 279.99);
--popover-foreground: oklch(1 0 0);
/* Primary */
--primary: oklch(0.7125 0.1753 155.24);
--primary-foreground: oklch(1 0 0);
/* Secondary */
--secondary: oklch(0.8633 0.1764 89.91);
--secondary-foreground: oklch(0.2122 0.0178 279.8);
/* Muted */
--muted: oklch(0.9672 0 0);
--muted-foreground: oklch(0.5948 0.0257 252.3);
/* Accent */
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
/* Destructive */
--destructive: oklch(0.6368 0.2488 28.19);
/* UI Elements */
--border: oklch(0.2986 0.0109 278.2);
--input: oklch(0.2559 0.0207 280.75);
--ring: oklch(0.7125 0.1753 155.24);
/* Chart Colors */
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
/* Sidebar */
--sidebar: oklch(0.2122 0.0178 279.8);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.7125 0.1753 155.24);
--sidebar-primary-foreground: oklch(1 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.7125 0.1753 155.24);
/* Custom Application Variables */
--c1: oklch(0.8337 0.0056 17.27);
--c2: oklch(0.8884 0.0166 182.67);
}
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}
@theme inline {
--font-sans: var(--jost-font);
--font-mono: var(--cousine-font);
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2);
--color-chart-3: var(--chart-3);
--color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5);
--color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
--color-c1: var(--c1);
--color-c2: var(--c2);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
}
@utility start {
display: flex;
align-items: center;
justify-content: flex-start;
}
@utility btwn {
display: flex;
align-items: center;
justify-content: space-between;
}
@utility center {
display: flex;
align-items: center;
justify-content: center;
}
@utility end {
display: flex;
align-items: center;
justify-content: flex-end;
}
@utility max-w-unset {
max-width: unset;
}
@utility max-h-unset {
max-height: unset;
}
@utility h-unset {
height: unset;
}
@utility w-unset {
width: unset;
}With this setup, you can now use colors directly in your classes:
// Tailwind 4 - Direct usage
<div className="bg-primary text-primary-foreground">
<div className="bg-c1 text-c2">Configuration File (Before Tailwind 4)
For projects using Tailwind CSS 3 or earlier, configure colors in your Tailwind config file. Choose your configuration format:
const plugin = require("tailwindcss/plugin");
const defaultTheme = require("tailwindcss/defaultTheme");
/** @type {import('tailwindcss').Config} */
module.exports = {
darkMode: ["class"],
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
theme: {
extend: {
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
xl: "calc(var(--radius) + 4px)",
},
colors: {
background: "var(--background)",
foreground: "var(--foreground)",
card: {
DEFAULT: "var(--card)",
foreground: "var(--card-foreground)",
},
popover: {
DEFAULT: "var(--popover)",
foreground: "var(--popover-foreground)",
},
primary: {
DEFAULT: "var(--primary)",
foreground: "var(--primary-foreground)",
},
secondary: {
DEFAULT: "var(--secondary)",
foreground: "var(--secondary-foreground)",
},
muted: {
DEFAULT: "var(--muted)",
foreground: "var(--muted-foreground)",
},
accent: {
DEFAULT: "var(--accent)",
foreground: "var(--accent-foreground)",
},
destructive: {
DEFAULT: "var(--destructive)",
foreground: "var(--destructive-foreground)",
},
border: "var(--border)",
input: "var(--input)",
ring: "var(--ring)",
chart: {
1: "var(--chart-1)",
2: "var(--chart-2)",
3: "var(--chart-3)",
4: "var(--chart-4)",
5: "var(--chart-5)",
},
fontFamily: {
sans: ["Jost", ...defaultTheme.fontFamily.sans],
mono: ["Cousine", ...defaultTheme.fontFamily.mono],
},
sidebar: {
DEFAULT: "var(--sidebar)",
foreground: "var(--sidebar-foreground)",
primary: "var(--sidebar-primary)",
"primary-foreground": "var(--sidebar-primary-foreground)",
accent: "var(--sidebar-accent)",
"accent-foreground": "var(--sidebar-accent-foreground)",
border: "var(--sidebar-border)",
ring: "var(--sidebar-ring)",
},
},
},
},
plugins: [require("tailwindcss-animate")],
};import plugin from "tailwindcss/plugin";
import defaultTheme from "tailwindcss/defaultTheme";
/** @type {import('tailwindcss').Config} */
export default {
darkMode: ["class"],
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
theme: {
extend: {
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
xl: "calc(var(--radius) + 4px)",
},
colors: {
background: "var(--background)",
foreground: "var(--foreground)",
card: {
DEFAULT: "var(--card)",
foreground: "var(--card-foreground)",
},
popover: {
DEFAULT: "var(--popover)",
foreground: "var(--popover-foreground)",
},
primary: {
DEFAULT: "var(--primary)",
foreground: "var(--primary-foreground)",
},
secondary: {
DEFAULT: "var(--secondary)",
foreground: "var(--secondary-foreground)",
},
muted: {
DEFAULT: "var(--muted)",
foreground: "var(--muted-foreground)",
},
accent: {
DEFAULT: "var(--accent)",
foreground: "var(--accent-foreground)",
},
destructive: {
DEFAULT: "var(--destructive)",
foreground: "var(--destructive-foreground)",
},
border: "var(--border)",
input: "var(--input)",
ring: "var(--ring)",
chart: {
1: "var(--chart-1)",
2: "var(--chart-2)",
3: "var(--chart-3)",
4: "var(--chart-4)",
5: "var(--chart-5)",
},
sidebar: {
DEFAULT: "var(--sidebar)",
foreground: "var(--sidebar-foreground)",
primary: "var(--sidebar-primary)",
"primary-foreground": "var(--sidebar-primary-foreground)",
accent: "var(--sidebar-accent)",
"accent-foreground": "var(--sidebar-accent-foreground)",
border: "var(--sidebar-border)",
ring: "var(--sidebar-ring)",
},
},
},
},
plugins: [require("tailwindcss-animate")],
};import plugin from "tailwindcss/plugin";
import defaultTheme from "tailwindcss/defaultTheme";
/** @type {import('tailwindcss').Config} */
export default {
darkMode: ["class"],
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
theme: {
extend: {
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
xl: "calc(var(--radius) + 4px)",
},
colors: {
background: "var(--background)",
foreground: "var(--foreground)",
card: {
DEFAULT: "var(--card)",
foreground: "var(--card-foreground)",
},
popover: {
DEFAULT: "var(--popover)",
foreground: "var(--popover-foreground)",
},
primary: {
DEFAULT: "var(--primary)",
foreground: "var(--primary-foreground)",
},
secondary: {
DEFAULT: "var(--secondary)",
foreground: "var(--secondary-foreground)",
},
muted: {
DEFAULT: "var(--muted)",
foreground: "var(--muted-foreground)",
},
accent: {
DEFAULT: "var(--accent)",
foreground: "var(--accent-foreground)",
},
destructive: {
DEFAULT: "var(--destructive)",
foreground: "var(--destructive-foreground)",
},
border: "var(--border)",
input: "var(--input)",
ring: "var(--ring)",
chart: {
1: "var(--chart-1)",
2: "var(--chart-2)",
3: "var(--chart-3)",
4: "var(--chart-4)",
5: "var(--chart-5)",
},
sidebar: {
DEFAULT: "var(--sidebar)",
foreground: "var(--sidebar-foreground)",
primary: "var(--sidebar-primary)",
"primary-foreground": "var(--sidebar-primary-foreground)",
accent: "var(--sidebar-accent)",
"accent-foreground": "var(--sidebar-accent-foreground)",
border: "var(--sidebar-border)",
ring: "var(--sidebar-ring)",
},
},
},
},
plugins: [await import("tailwindcss-animate")],
};import type { Config } from "tailwindcss";
import plugin from "tailwindcss/plugin";
import defaultTheme from "tailwindcss/defaultTheme";
const config: Config = {
darkMode: ["class"],
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
theme: {
extend: {
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
xl: "calc(var(--radius) + 4px)",
},
colors: {
background: "var(--background)",
foreground: "var(--foreground)",
card: {
DEFAULT: "var(--card)",
foreground: "var(--card-foreground)",
},
popover: {
DEFAULT: "var(--popover)",
foreground: "var(--popover-foreground)",
},
primary: {
DEFAULT: "var(--primary)",
foreground: "var(--primary-foreground)",
},
secondary: {
DEFAULT: "var(--secondary)",
foreground: "var(--secondary-foreground)",
},
muted: {
DEFAULT: "var(--muted)",
foreground: "var(--muted-foreground)",
},
accent: {
DEFAULT: "var(--accent)",
foreground: "var(--accent-foreground)",
},
destructive: {
DEFAULT: "var(--destructive)",
foreground: "var(--destructive-foreground)",
},
border: "var(--border)",
input: "var(--input)",
ring: "var(--ring)",
chart: {
1: "var(--chart-1)",
2: "var(--chart-2)",
3: "var(--chart-3)",
4: "var(--chart-4)",
5: "var(--chart-5)",
},
sidebar: {
DEFAULT: "var(--sidebar)",
foreground: "var(--sidebar-foreground)",
primary: "var(--sidebar-primary)",
"primary-foreground": "var(--sidebar-primary-foreground)",
accent: "var(--sidebar-accent)",
"accent-foreground": "var(--sidebar-accent-foreground)",
border: "var(--sidebar-border)",
ring: "var(--sidebar-ring)",
},
},
},
},
plugins: [require("tailwindcss-animate")],
};
export default config;Then add the CSS variables to your global.css file:
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--radius: 0.625rem;
--background: oklch(0.2122 0.0178 279.8);
--foreground: oklch(1 0 0);
--card: oklch(0.2559 0.0207 280.75);
--card-foreground: oklch(1 0 0);
--popover: oklch(0.1856 0.0325 279.99);
--popover-foreground: oklch(1 0 0);
--primary: oklch(0.7125 0.1753 155.24);
--primary-foreground: oklch(1 0 0);
--secondary: oklch(0.8633 0.1764 89.91);
--secondary-foreground: oklch(0.2122 0.0178 279.8);
--muted: oklch(0.9672 0 0);
--muted-foreground: oklch(0.5948 0.0257 252.3);
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.6368 0.2488 28.19);
--border: oklch(0.2986 0.0109 278.2);
--input: oklch(0.2559 0.0207 280.75);
--ring: oklch(0.7125 0.1753 155.24);
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
--sidebar: oklch(0.2122 0.0178 279.8);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.7125 0.1753 155.24);
--sidebar-primary-foreground: oklch(1 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.7125 0.1753 155.24);
--c1: oklch(0.8337 0.0056 17.27);
--c2: oklch(0.8884 0.0166 182.67);
}
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}Color Reference
Semantic Colors
| Variable | Swatch | Purpose | OKLCH Value |
|---|---|---|---|
--background | Main background color | oklch(0.2122 0.0178 279.8) | |
--foreground | Main text color | oklch(1 0 0) | |
--primary | Primary brand color (Green) | oklch(0.7125 0.1753 155.24) | |
--secondary | Secondary brand color (Yellow) | oklch(0.8633 0.1764 89.91) | |
--destructive | Error/destructive actions | oklch(0.6368 0.2488 28.19) | |
--muted | Muted/subtle elements | oklch(0.9672 0 0) | |
--accent | Accent elements | oklch(0.97 0 0) |
Component Colors
| Variable | Swatch | Purpose |
|---|---|---|
--card | Card background | |
--card-foreground | Card text color | |
--popover | Popover background | |
--popover-foreground | Popover text color | |
--border | Border color | |
--input | Input background | |
--ring | Focus ring color |
Chart Colors
Five chart colors are available for data visualization:
| Variable | Swatch | Description | OKLCH Value |
|---|---|---|---|
--chart-1 | Orange | oklch(0.646 0.222 41.116) | |
--chart-2 | Cyan | oklch(0.6 0.118 184.704) | |
--chart-3 | Blue | oklch(0.398 0.07 227.392) | |
--chart-4 | Green-Yellow | oklch(0.828 0.189 84.429) | |
--chart-5 | Yellow-Orange | oklch(0.769 0.188 70.08) |
// Usage in components
const chartColors = [
'var(--chart-1)', // Orange
'var(--chart-2)', // Cyan
'var(--chart-3)', // Blue
'var(--chart-4)', // Green-Yellow
'var(--chart-5)', // Yellow-Orange
];Usage Examples
Using in Tailwind Classes
<div className="bg-primary text-primary-foreground">
Primary Button
</div>
<div className="bg-secondary text-secondary-foreground">
Secondary Button
</div>
<div className="border-border bg-card text-card-foreground">
Card Component
</div>Using as CSS Variables
.custom-component {
background-color: var(--primary);
color: var(--primary-foreground);
border: 1px solid var(--border);
border-radius: var(--radius);
}Dynamic Color Application
<div style={{ backgroundColor: 'var(--chart-3)' }}>
Dynamic Chart Element
</div>Adding Custom Colors
When adding new custom colors to your application, it's recommended to follow this naming convention:
:root {
/* Existing colors... */
/* Custom colors - use c1, c2, c3... cn */
--c1: oklch(0.8337 0.0056 17.27);
--c2: oklch(0.8884 0.0166 182.67);
--c3: oklch(0.75 0.15 120);
/* Add more as needed */
}
/* For Tailwind 4: Add to @theme inline */
@theme inline {
--color-c1: var(--c1);
--color-c2: var(--c2);
--color-c3: var(--c3);
}Usage:
// Tailwind 4
<div className="bg-c1 text-c2">Custom colors</div>
// Tailwind 3 and earlier (bracket notation)
<div className="bg-[var(--c1)] text-[var(--c2)]">Custom colors</div>Why OKLCH?
- Better perceptual uniformity than RGB or HSL
- More consistent lightness across different hues
- Easier to create harmonious color palettes
- Future-proof as browsers improve color space support
Customization
To customize colors for your specific application:
- Modify the OKLCH values in your CSS variables
- Use a color picker that supports OKLCH format (e.g., OKLCH Color Picker)
- Ensure sufficient contrast between foreground and background colors
- Test colors in your target environments
OKLCH Format
OKLCH consists of three values:
- Lightness (0-1): Brightness of the color
- Chroma (0-0.4): Colorfulness/saturation
- Hue (0-360): Color angle on the color wheel
Example: oklch(0.7125 0.1753 155.24)
- Lightness: 71.25%
- Chroma: 17.53%
- Hue: 155.24° (green)