Add a square corners toggle for a border-radius-free interface

This commit is contained in:
Steven RYDELL
2026-07-29 08:52:37 +02:00
parent 13c21fc6d5
commit 272502e0a3
4 changed files with 42 additions and 4 deletions
+10 -1
View File
@@ -7,7 +7,7 @@
import { Link, useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import * as LucideIcons from 'lucide-react';
const { Sun, Moon, User, LogOut, Check, Menu, Sparkles, Search } = LucideIcons;
const { Sun, Moon, User, LogOut, Check, Menu, Sparkles, Search, Radius } = LucideIcons;
import { Button } from '@/components/ui/button';
import { CommandPalette } from '@/components/common/CommandPalette';
import {
@@ -43,6 +43,8 @@ export function TopBar() {
const navigate = useNavigate();
const theme = useUIStore((s) => s.theme);
const toggleTheme = useUIStore((s) => s.toggleTheme);
const radius = useUIStore((s) => s.radius);
const toggleRadius = useUIStore((s) => s.toggleRadius);
const toggleSidebar = useUIStore((s) => s.toggleSidebar);
const setActiveSection = useUIStore((s) => s.setActiveSection);
const accounts = useAuthStore((s) => s.accounts);
@@ -189,6 +191,13 @@ export function TopBar() {
</>
)}
<DropdownMenuItem onClick={toggleRadius}>
<Radius className="mr-2 h-4 w-4" />
{t('squareCorners', 'Square corners')}
{radius === 'square' && <Check className="ml-auto h-4 w-4" />}
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
onClick={() => {
const endSessionEndpoint = useAuthStore.getState().endSessionEndpoint;
+1
View File
@@ -337,6 +337,7 @@
"periodValue": "Period value"
},
"sections": "Sections",
"squareCorners": "Square corners",
"toggleTheme": "Toggle theme",
"tracing": {
"addFilter": "Add filter",
+9 -3
View File
@@ -90,6 +90,12 @@
--chart-5: 340 75% 60%;
}
/* Square mode: every radius token derives multiplicatively from --radius,
so zeroing it here flattens all corners (rounded-full stays untouched). */
:root[data-radius='square'] {
--radius: 0px;
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
@@ -127,10 +133,10 @@
--color-chart-4: hsl(var(--chart-4));
--color-chart-5: hsl(var(--chart-5));
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-sm: calc(var(--radius) * 0.5);
--radius-md: calc(var(--radius) * 0.75);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--radius-xl: calc(var(--radius) * 1.5);
--animate-collapsible-down: collapsible-down 0.2s ease-out;
--animate-collapsible-up: collapsible-up 0.2s ease-out;
+22
View File
@@ -8,14 +8,18 @@ import { create } from 'zustand';
import { persist } from 'zustand/middleware';
type Theme = 'light' | 'dark';
type Radius = 'rounded' | 'square';
interface UIState {
theme: Theme;
radius: Radius;
sidebarOpen: boolean;
activeSection: string;
toggleTheme: () => void;
setTheme: (theme: Theme) => void;
toggleRadius: () => void;
setRadius: (radius: Radius) => void;
toggleSidebar: () => void;
setSidebarOpen: (open: boolean) => void;
setActiveSection: (section: string) => void;
@@ -29,11 +33,16 @@ function applyThemeClass(theme: Theme) {
}
}
function applyRadiusAttribute(radius: Radius) {
document.documentElement.dataset.radius = radius;
}
export const useUIStore = create<UIState>()(
persist(
(set, get) => ({
theme:
typeof window !== 'undefined' && window.matchMedia?.('(prefers-color-scheme: dark)').matches ? 'dark' : 'light',
radius: 'rounded',
sidebarOpen: typeof window !== 'undefined' ? (window.matchMedia?.('(min-width: 768px)').matches ?? true) : true,
activeSection: '',
@@ -48,6 +57,17 @@ export const useUIStore = create<UIState>()(
set({ theme });
},
toggleRadius: () => {
const next = get().radius === 'rounded' ? 'square' : 'rounded';
applyRadiusAttribute(next);
set({ radius: next });
},
setRadius: (radius) => {
applyRadiusAttribute(radius);
set({ radius });
},
toggleSidebar: () => {
set({ sidebarOpen: !get().sidebarOpen });
},
@@ -64,11 +84,13 @@ export const useUIStore = create<UIState>()(
name: 'stalwart-ui',
partialize: (state) => ({
theme: state.theme,
radius: state.radius,
}),
onRehydrateStorage: () => {
return (state) => {
if (state) {
applyThemeClass(state.theme);
applyRadiusAttribute(state.radius ?? 'rounded');
}
};
},