alpe-temp 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/frontend-project/src/App.css +1 -0
  2. package/frontend-project/src/Auth/Login.jsx +85 -0
  3. package/frontend-project/src/Auth/Register.jsx +183 -0
  4. package/frontend-project/src/Intro.jsx +33 -0
  5. package/frontend-project/src/LayOut.jsx +38 -0
  6. package/frontend-project/src/api/ApiClient.js +92 -0
  7. package/frontend-project/src/assets/hero.png +0 -0
  8. package/frontend-project/src/assets/react.svg +1 -0
  9. package/frontend-project/src/assets/vite.svg +1 -0
  10. package/frontend-project/src/components/Aside.jsx +9 -0
  11. package/frontend-project/src/components/Button.jsx +100 -0
  12. package/frontend-project/src/components/Card.jsx +104 -0
  13. package/frontend-project/src/components/FormField.jsx +129 -0
  14. package/frontend-project/src/components/Modal.jsx +106 -0
  15. package/frontend-project/src/components/Table.jsx +127 -0
  16. package/frontend-project/src/components/Toast.jsx +64 -0
  17. package/frontend-project/src/components/index.js +14 -0
  18. package/frontend-project/src/config.js +66 -0
  19. package/frontend-project/src/design.js +115 -0
  20. package/frontend-project/src/index.css +60 -0
  21. package/frontend-project/src/layouts/BottomNav.jsx +156 -0
  22. package/frontend-project/src/layouts/TopNav.jsx +150 -0
  23. package/frontend-project/src/layouts/useShell.js +44 -0
  24. package/frontend-project/src/main.jsx +41 -0
  25. package/frontend-project/src/pages/Department.jsx +188 -0
  26. package/frontend-project/src/pages/Employee.jsx +274 -0
  27. package/frontend-project/src/pages/Home.jsx +79 -0
  28. package/frontend-project/src/pages/Profile.jsx +9 -0
  29. package/frontend-project/src/pages/Register.jsx +57 -0
  30. package/frontend-project/src/pages/Reports.jsx +91 -0
  31. package/frontend-project/src/pages/Salary.jsx +264 -0
  32. package/frontend-project/src/themes.js +175 -0
  33. package/package.json +1 -1
@@ -0,0 +1,104 @@
1
+ // ═══════════════════════════════════════════════════════════════════════════
2
+ // 🃏 CARD — Reusable card & stat card with config-driven styling
3
+ // ═══════════════════════════════════════════════════════════════════════════
4
+ //
5
+ // HOW TO USE:
6
+ // <Card title="Users" action={<Button>Add</Button>}>
7
+ // <p>Content goes here</p>
8
+ // </Card>
9
+ //
10
+ // <StatCard label="Total Employees" value="150" trend="+12%" />
11
+ //
12
+ // PROPS (Card):
13
+ // title: card heading text
14
+ // subtitle: smaller text below title
15
+ // action: element to show on the right (e.g. a Button)
16
+ // children: card body content
17
+ // className: additional CSS classes
18
+ //
19
+ // PROPS (StatCard):
20
+ // label: stat label
21
+ // value: stat value (string or number)
22
+ // trend: optional trend text (e.g. "+12%")
23
+ // trendUp: whether trend is positive (true=green, false=red)
24
+ // loading: show skeleton placeholder
25
+ // className: additional CSS classes
26
+ //
27
+ // CONFIG INTEGRATION:
28
+ // - Background from var(--color-card)
29
+ // - Border from var(--color-border)
30
+ //
31
+ // ═══════════════════════════════════════════════════════════════════════════
32
+
33
+ import React from 'react';
34
+ import { config } from '../config';
35
+ import { getRoundedClass } from '../design';
36
+
37
+ export function Card({ title, subtitle, action, children, className = '' }) {
38
+ const roundClass = getRoundedClass(config.rounded);
39
+
40
+ return (
41
+ <div
42
+ className={`p-5 ${roundClass} ${className}`}
43
+ style={{
44
+ backgroundColor: 'var(--color-card)',
45
+ border: '1px solid var(--color-border)',
46
+ }}
47
+ >
48
+ {(title || action) && (
49
+ <div className="flex items-start justify-between mb-4">
50
+ <div>
51
+ {title && (
52
+ <h3 className="text-[13px] font-semibold" style={{ color: 'var(--color-text)' }}>
53
+ {title}
54
+ </h3>
55
+ )}
56
+ {subtitle && (
57
+ <p className="text-[11px] mt-0.5" style={{ color: 'var(--color-text-muted)' }}>
58
+ {subtitle}
59
+ </p>
60
+ )}
61
+ </div>
62
+ {action && <div className="flex-shrink-0">{action}</div>}
63
+ </div>
64
+ )}
65
+ {children}
66
+ </div>
67
+ );
68
+ }
69
+
70
+ export function StatCard({ label, value, trend, trendUp = true, loading = false, className = '' }) {
71
+ const roundClass = getRoundedClass(config.rounded);
72
+
73
+ return (
74
+ <div
75
+ className={`p-5 flex flex-col gap-3 ${roundClass} ${className}`}
76
+ style={{
77
+ backgroundColor: 'var(--color-card)',
78
+ border: '1px solid var(--color-border)',
79
+ }}
80
+ >
81
+ <div className="flex items-center justify-between">
82
+ <span className="text-[12px] font-semibold uppercase tracking-wide" style={{ color: 'var(--color-text-muted)' }}>
83
+ {label}
84
+ </span>
85
+ </div>
86
+ {loading ? (
87
+ <div className="h-8 w-24 animate-pulse" style={{ backgroundColor: 'var(--color-border)' }} />
88
+ ) : (
89
+ <span className="text-[32px] font-bold leading-none" style={{ color: 'var(--color-text)' }}>
90
+ {value ?? '—'}
91
+ </span>
92
+ )}
93
+ {trend && (
94
+ <span
95
+ className={`text-[11px] font-semibold px-2 py-0.5 w-fit ${
96
+ trendUp ? 'bg-emerald-50 text-emerald-600' : 'bg-red-50 text-red-500'
97
+ }`}
98
+ >
99
+ {trend}
100
+ </span>
101
+ )}
102
+ </div>
103
+ );
104
+ }
@@ -0,0 +1,129 @@
1
+ // ═══════════════════════════════════════════════════════════════════════════
2
+ // 📝 FORM FIELD — Reusable form inputs with labels & validation
3
+ // ═══════════════════════════════════════════════════════════════════════════
4
+ //
5
+ // HOW TO USE:
6
+ // <FormField label="Email" required error={errors.email}>
7
+ // <FormField.Input type="email" value={email} onChange={setEmail} />
8
+ // </FormField>
9
+ //
10
+ // <FormField label="Role">
11
+ // <FormField.Select options={['admin', 'user']} value={role} onChange={setRole} />
12
+ // </FormField>
13
+ //
14
+ // <FormField label="Bio">
15
+ // <FormField.Textarea value={bio} onChange={setBio} />
16
+ // </FormField>
17
+ //
18
+ // PROPS:
19
+ // label: field label text
20
+ // required: show red asterisk
21
+ // error: error message string
22
+ // children: input element(s)
23
+ // className: additional CSS classes
24
+ //
25
+ // SUB-COMPONENTS:
26
+ // FormField.Input → <input>
27
+ // FormField.Select → <select>
28
+ // FormField.Textarea → <textarea>
29
+ //
30
+ // CONFIG INTEGRATION:
31
+ // - Rounded corners from config.js → --radius CSS variable
32
+ // - Colors from CSS variables
33
+ //
34
+ // ═══════════════════════════════════════════════════════════════════════════
35
+
36
+ import React from 'react';
37
+ import { config } from '../config';
38
+ import { getRoundedClass } from '../design';
39
+
40
+ // ─── BASE INPUT STYLES ──────────────────────────────────────────────────────
41
+ const INPUT_BASE =
42
+ 'w-full px-3 py-2 text-[13px] font-medium ' +
43
+ 'border transition-colors duration-150 ' +
44
+ 'placeholder:text-gray-400 focus:outline-none focus:ring-1 ' +
45
+ 'disabled:bg-gray-50 disabled:text-gray-400';
46
+
47
+ // ─── MAIN FORM FIELD COMPONENT ──────────────────────────────────────────────
48
+ export default function FormField({ label, required = false, error, children, className = '' }) {
49
+ return (
50
+ <div className={`flex flex-col gap-1 ${className}`}>
51
+ {label && (
52
+ <label className="text-[12px] font-semibold select-none" style={{ color: 'var(--color-text-muted)' }}>
53
+ {label}
54
+ {required && <span className="text-red-400 ml-0.5">*</span>}
55
+ </label>
56
+ )}
57
+ {children}
58
+ {error && <p className="text-[11px] text-red-500 font-medium">{error}</p>}
59
+ </div>
60
+ );
61
+ }
62
+
63
+ // ─── TEXT INPUT ──────────────────────────────────────────────────────────────
64
+ FormField.Input = function FieldInput({ onChange, className = '', ...props }) {
65
+ const roundClass = getRoundedClass(config.rounded);
66
+ return (
67
+ <input
68
+ className={`${INPUT_BASE} ${roundClass} ${className}`}
69
+ style={{
70
+ backgroundColor: 'var(--color-card)',
71
+ borderColor: 'var(--color-border)',
72
+ color: 'var(--color-text)',
73
+ }}
74
+ onChange={(e) => onChange?.(e.target.value, e)}
75
+ {...props}
76
+ />
77
+ );
78
+ };
79
+
80
+ // ─── SELECT DROPDOWN ─────────────────────────────────────────────────────────
81
+ FormField.Select = function FieldSelect({ value, onChange, options = [], placeholder, className = '', ...props }) {
82
+ const roundClass = getRoundedClass(config.rounded);
83
+ const normalised = options.map((o) =>
84
+ typeof o === 'string' ? { value: o, label: o } : o
85
+ );
86
+
87
+ return (
88
+ <select
89
+ value={value}
90
+ onChange={(e) => onChange?.(e.target.value, e)}
91
+ className={`${INPUT_BASE} ${roundClass} ${className}`}
92
+ style={{
93
+ backgroundColor: 'var(--color-card)',
94
+ borderColor: 'var(--color-border)',
95
+ color: 'var(--color-text)',
96
+ }}
97
+ {...props}
98
+ >
99
+ {placeholder && (
100
+ <option value="" disabled>
101
+ {placeholder}
102
+ </option>
103
+ )}
104
+ {normalised.map((o) => (
105
+ <option key={o.value} value={o.value}>
106
+ {o.label}
107
+ </option>
108
+ ))}
109
+ </select>
110
+ );
111
+ };
112
+
113
+ // ─── TEXTAREA ────────────────────────────────────────────────────────────────
114
+ FormField.Textarea = function FieldTextarea({ onChange, className = '', ...props }) {
115
+ const roundClass = getRoundedClass(config.rounded);
116
+ return (
117
+ <textarea
118
+ rows={3}
119
+ className={`${INPUT_BASE} resize-none ${roundClass} ${className}`}
120
+ style={{
121
+ backgroundColor: 'var(--color-card)',
122
+ borderColor: 'var(--color-border)',
123
+ color: 'var(--color-text)',
124
+ }}
125
+ onChange={(e) => onChange?.(e.target.value, e)}
126
+ {...props}
127
+ />
128
+ );
129
+ };
@@ -0,0 +1,106 @@
1
+ // ═══════════════════════════════════════════════════════════════════════════
2
+ // 🪟 MODAL — Backdrop modal with config-driven styling
3
+ // ═══════════════════════════════════════════════════════════════════════════
4
+ //
5
+ // WHAT THIS IS:
6
+ // A modal dialog that appears over everything with a dark backdrop.
7
+ // Press Escape or click outside to close.
8
+ //
9
+ // HOW TO USE:
10
+ // <Modal open={isOpen} onClose={() => setOpen(false)} title="Edit Item">
11
+ // <p>Modal content here</p>
12
+ // <Modal.Footer>
13
+ // <Button onClick={() => setOpen(false)}>Cancel</Button>
14
+ // </Modal.Footer>
15
+ // </Modal>
16
+ //
17
+ // PROPS:
18
+ // open: boolean (show/hide)
19
+ // onClose: function to close
20
+ // title: modal header text
21
+ // size: 'sm' | 'md' | 'lg' | 'xl' | '2xl'
22
+ // children: modal body
23
+ // className: additional CSS classes
24
+ //
25
+ // CONFIG INTEGRATION:
26
+ // - Rounded corners from config.js
27
+ // - Colors from CSS variables
28
+ //
29
+ // ═══════════════════════════════════════════════════════════════════════════
30
+
31
+ import React, { useEffect } from 'react';
32
+ import { createPortal } from 'react-dom';
33
+ import { X } from 'lucide-react';
34
+ import { config } from '../config';
35
+ import { getRoundedClass } from '../design';
36
+
37
+ const SIZES = {
38
+ sm: 'max-w-sm',
39
+ md: 'max-w-md',
40
+ lg: 'max-w-lg',
41
+ xl: 'max-w-xl',
42
+ '2xl': 'max-w-2xl',
43
+ };
44
+
45
+ export default function Modal({ open, onClose, title, size = 'md', children, className = '' }) {
46
+ const roundClass = getRoundedClass(config.rounded);
47
+
48
+ // Close on Escape key
49
+ useEffect(() => {
50
+ if (!open) return;
51
+ const handler = (e) => { if (e.key === 'Escape') onClose(); };
52
+ window.addEventListener('keydown', handler);
53
+ return () => window.removeEventListener('keydown', handler);
54
+ }, [open, onClose]);
55
+
56
+ if (!open) return null;
57
+
58
+ return createPortal(
59
+ <div className="fixed inset-0 z-[9999] flex items-center justify-center p-4" role="dialog" aria-modal="true">
60
+ {/* Backdrop */}
61
+ <div
62
+ className="absolute inset-0 bg-black/40 backdrop-blur-[2px]"
63
+ onClick={onClose}
64
+ />
65
+ {/* Modal content */}
66
+ <div
67
+ className={`relative shadow-xl w-full ${SIZES[size] ?? SIZES.md} ${roundClass} ${className}`}
68
+ style={{
69
+ backgroundColor: 'var(--color-card)',
70
+ border: '1px solid var(--color-border)',
71
+ }}
72
+ >
73
+ {title && (
74
+ <div className="flex items-center justify-between px-5 py-4 border-b" style={{ borderColor: 'var(--color-border)' }}>
75
+ <h2 className="text-[14px] font-semibold" style={{ color: 'var(--color-text)' }}>
76
+ {title}
77
+ </h2>
78
+ <button
79
+ onClick={onClose}
80
+ className="w-7 h-7 flex items-center justify-center transition-colors hover:opacity-70"
81
+ style={{ color: 'var(--color-text-muted)' }}
82
+ >
83
+ <X className="w-4 h-4" />
84
+ </button>
85
+ </div>
86
+ )}
87
+ <div className="px-5 py-4">{children}</div>
88
+ </div>
89
+ </div>,
90
+ document.body
91
+ );
92
+ }
93
+
94
+ // ─── MODAL FOOTER ────────────────────────────────────────────────────────────
95
+ // Use inside <Modal> to add a footer with action buttons.
96
+ //
97
+ Modal.Footer = function ModalFooter({ children }) {
98
+ return (
99
+ <div
100
+ className="flex justify-end gap-2 pt-3 mt-2 border-t"
101
+ style={{ borderColor: 'var(--color-border)' }}
102
+ >
103
+ {children}
104
+ </div>
105
+ );
106
+ };
@@ -0,0 +1,127 @@
1
+ // ═══════════════════════════════════════════════════════════════════════════
2
+ // 📊 TABLE — Reusable data table with loading & empty states
3
+ // ═══════════════════════════════════════════════════════════════════════════
4
+ //
5
+ // HOW TO USE:
6
+ // <Table
7
+ // columns={[
8
+ // { key: 'name', label: 'Name' },
9
+ // { key: 'email', label: 'Email', align: 'center' },
10
+ // { key: 'status', label: 'Status', render: (val) => <Badge>{val}</Badge> },
11
+ // ]}
12
+ // data={users}
13
+ // rowKey="id"
14
+ // />
15
+ //
16
+ // PROPS:
17
+ // columns: array of { key, label, align?, render? }
18
+ // data: array of row objects
19
+ // rowKey: unique key per row (default: 'id')
20
+ // loading: show loading skeleton
21
+ // emptyMessage: text when no data
22
+ // className: additional CSS classes
23
+ // maxHeight: max scrollable height (default: '360px')
24
+ //
25
+ // CONFIG INTEGRATION:
26
+ // - Rounded corners from config.js
27
+ // - Colors from CSS variables
28
+ //
29
+ // ═══════════════════════════════════════════════════════════════════════════
30
+
31
+ import React from 'react';
32
+ import { config } from '../config';
33
+ import { getRoundedClass } from '../design';
34
+
35
+ export default function Table({
36
+ columns,
37
+ data = [],
38
+ rowKey = 'id',
39
+ loading = false,
40
+ emptyMessage = 'No data found',
41
+ className = '',
42
+ maxHeight = '360px',
43
+ }) {
44
+ const ALIGN = { left: 'text-left', center: 'text-center', right: 'text-right' };
45
+ const roundClass = getRoundedClass(config.rounded);
46
+
47
+ return (
48
+ <div
49
+ className={`w-full overflow-auto border ${roundClass} ${className}`}
50
+ style={{ maxHeight, borderColor: 'var(--color-border)' }}
51
+ >
52
+ <table className="w-full min-w-max">
53
+ {/* ─── HEADER ──────────────────────────────────────────────────── */}
54
+ <thead className="sticky top-0 z-10">
55
+ <tr
56
+ className="border-b"
57
+ style={{
58
+ backgroundColor: 'var(--color-surface)',
59
+ borderColor: 'var(--color-border)',
60
+ }}
61
+ >
62
+ {columns.map((col) => (
63
+ <th
64
+ key={col.key}
65
+ className={`px-4 py-3 text-[11px] font-semibold uppercase tracking-wider whitespace-nowrap ${ALIGN[col.align ?? 'left']}`}
66
+ style={{ color: 'var(--color-text-muted)' }}
67
+ >
68
+ {col.label}
69
+ </th>
70
+ ))}
71
+ </tr>
72
+ </thead>
73
+
74
+ {/* ─── BODY ────────────────────────────────────────────────────── */}
75
+ <tbody className="divide-y" style={{ backgroundColor: 'var(--color-card)', borderColor: 'var(--color-border)' }}>
76
+ {/* Loading skeleton */}
77
+ {loading ? (
78
+ Array.from({ length: 5 }).map((_, i) => (
79
+ <tr key={i}>
80
+ {columns.map((col) => (
81
+ <td key={col.key} className="px-4 py-3">
82
+ <div
83
+ className="h-3 animate-pulse"
84
+ style={{
85
+ width: `${60 + (i * 13 + col.key.length * 7) % 30}%`,
86
+ backgroundColor: 'var(--color-border)',
87
+ }}
88
+ />
89
+ </td>
90
+ ))}
91
+ </tr>
92
+ ))
93
+ ) : data.length === 0 ? (
94
+ // Empty state
95
+ <tr>
96
+ <td
97
+ colSpan={columns.length}
98
+ className="px-4 py-10 text-center text-[13px]"
99
+ style={{ color: 'var(--color-text-muted)' }}
100
+ >
101
+ {emptyMessage}
102
+ </td>
103
+ </tr>
104
+ ) : (
105
+ // Data rows
106
+ data.map((row, rowIdx) => (
107
+ <tr
108
+ key={row[rowKey] ?? rowIdx}
109
+ className="transition-colors duration-100 hover:bg-gray-50"
110
+ >
111
+ {columns.map((col) => (
112
+ <td
113
+ key={col.key}
114
+ className={`px-4 py-3 text-[12px] font-medium whitespace-nowrap ${ALIGN[col.align ?? 'left']}`}
115
+ style={{ color: 'var(--color-text)' }}
116
+ >
117
+ {col.render ? col.render(row[col.key], row) : (row[col.key] ?? '—')}
118
+ </td>
119
+ ))}
120
+ </tr>
121
+ ))
122
+ )}
123
+ </tbody>
124
+ </table>
125
+ </div>
126
+ );
127
+ }
@@ -0,0 +1,64 @@
1
+ import React, { createContext, useCallback, useContext, useState } from 'react';
2
+ import { CheckCircle, XCircle, Info, X } from 'lucide-react';
3
+
4
+ const ToastContext = createContext(null);
5
+
6
+ const ICONS = {
7
+ success: <CheckCircle className="w-4 h-4 text-emerald-500 flex-shrink-0" />,
8
+ error: <XCircle className="w-4 h-4 text-red-500 flex-shrink-0" />,
9
+ info: <Info className="w-4 h-4 text-blue-500 flex-shrink-0" />,
10
+ };
11
+
12
+ const BG = {
13
+ success: 'bg-white border-emerald-200',
14
+ error: 'bg-white border-red-200',
15
+ info: 'bg-white border-blue-200',
16
+ };
17
+
18
+ let _id = 0;
19
+
20
+ export function ToastProvider({ children }) {
21
+ const [toasts, setToasts] = useState([]);
22
+
23
+ const add = useCallback((message, type = 'info', duration = 3500) => {
24
+ const id = ++_id;
25
+ setToasts((prev) => [...prev, { id, message, type }]);
26
+ setTimeout(() => setToasts((prev) => prev.filter((t) => t.id !== id)), duration);
27
+ return id;
28
+ }, []);
29
+
30
+ const remove = useCallback((id) => setToasts((prev) => prev.filter((t) => t.id !== id)), []);
31
+
32
+ const api = {
33
+ success: (msg, dur) => add(msg, 'success', dur),
34
+ error: (msg, dur) => add(msg, 'error', dur),
35
+ info: (msg, dur) => add(msg, 'info', dur),
36
+ remove,
37
+ };
38
+
39
+ return (
40
+ <ToastContext.Provider value={api}>
41
+ {children}
42
+ <div className="fixed top-5 right-5 z-[9999] flex flex-col gap-2 pointer-events-none">
43
+ {toasts.map((t) => (
44
+ <div
45
+ key={t.id}
46
+ className={`pointer-events-auto flex items-center gap-3 px-4 py-3 border shadow-lg text-[13px] font-medium text-gray-700 min-w-[240px] max-w-xs ${BG[t.type]}`}
47
+ >
48
+ {ICONS[t.type]}
49
+ <span className="flex-1">{t.message}</span>
50
+ <button onClick={() => remove(t.id)} className="text-gray-300 hover:text-gray-500 transition-colors">
51
+ <X className="w-3.5 h-3.5" />
52
+ </button>
53
+ </div>
54
+ ))}
55
+ </div>
56
+ </ToastContext.Provider>
57
+ );
58
+ }
59
+
60
+ export function useToast() {
61
+ const ctx = useContext(ToastContext);
62
+ if (!ctx) throw new Error('useToast must be used inside <ToastProvider>');
63
+ return ctx;
64
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * src/components/ui/index.js
3
+ * Barrel export for all reusable UI primitives.
4
+ *
5
+ * Usage:
6
+ * import { Button, Table, Modal, FormField, Card, StatCard, ToastProvider, useToast } from '../components/ui';
7
+ */
8
+
9
+ export { default as Button } from './Button';
10
+ export { default as Table } from './Table';
11
+ export { default as Modal } from './Modal';
12
+ export { default as FormField } from './FormField';
13
+ export { Card, StatCard } from './Card';
14
+ export { ToastProvider, useToast } from './Toast';
@@ -0,0 +1,66 @@
1
+ // ═══════════════════════════════════════════════════════════════════════════
2
+ // 🎨 APP CONFIGURATION — Change ONE value to transform the whole app!
3
+ // ═══════════════════════════════════════════════════════════════════════════
4
+ //
5
+ // 📖 BEGINNER GUIDE:
6
+ //
7
+ // This is the ONLY file you need to touch to change the entire look
8
+ // and behavior of the app. Every page, component, and layout reads
9
+ // from this file.
10
+ //
11
+ // 🚀 QUICK EXAMPLES:
12
+ //
13
+ // 1. navigation: 'bottomnav' → navbar moves to the bottom (mobile style)
14
+ // 2. theme: 'midnight' → dark mode, instantly
15
+ // 3. rounded: 'none' → all buttons/cards become square
16
+ // 4. fontSize: 'large' → bigger text everywhere
17
+ // 5. colors.primary: '#FF0000' → brand color changes to red
18
+ //
19
+ // ═══════════════════════════════════════════════════════════════════════════
20
+
21
+ export const config = {
22
+ // ─── NAVIGATION ───────────────────────────────────────────────────────
23
+ // 'topnav' → horizontal bar at the TOP of the screen
24
+ // 'bottomnav' → bar at the BOTTOM (great for mobile / app-like feel)
25
+ navigation: 'bottomnav',
26
+
27
+ // ─── THEME ────────────────────────────────────────────────────────────
28
+ // Pick any theme name from themes.js. Available themes:
29
+ // 'forest' (green - default)
30
+ // 'midnight' (dark)
31
+ // 'slate' (blue-gray)
32
+ // 'rose' (pink/crimson)
33
+ // 'amber' (orange/gold)
34
+ // 'ocean' (navy/cyan)
35
+ // 'chalk' (black & white)
36
+ // 'violet' (purple)
37
+ theme: 'forest',
38
+
39
+ // ─── BORDER RADIUS ────────────────────────────────────────────────────
40
+ // Controls how rounded buttons, cards, inputs, and modals are.
41
+ // 'none' → completely square (sharp, modern)
42
+ // 'sm' → slightly rounded
43
+ // 'md' → moderately rounded (default feel)
44
+ // 'lg' → very rounded (pill-like)
45
+ // 'xl' → extra rounded
46
+ // 'full' → fully pill-shaped
47
+ rounded: 'md',
48
+
49
+ // ─── FONT SIZE ────────────────────────────────────────────────────────
50
+ // 'normal' → default text size
51
+ // 'large' → bigger text (easier to read)
52
+ fontSize: 'normal',
53
+
54
+ // ─── COLORS ───────────────────────────────────────────────────────────
55
+ // Leave empty ('') to use the theme's default colors.
56
+ // Set a value to override that specific color.
57
+ // Format: any valid CSS color (#hex, rgb, name)
58
+ colors: {
59
+ primary: '', // main brand color (buttons, links, active nav)
60
+ // secondary: '', // secondary accent
61
+ // success: '', // success states (green)
62
+ // danger: '', // error/danger states (red)
63
+ // warning: '', // warning states (yellow/amber)
64
+ // info: '', // info states (blue)
65
+ },
66
+ };