From cb0753c292e1c253c94ca7578a8e320409d8f8dc Mon Sep 17 00:00:00 2001 From: veryCrunchy Date: Sun, 1 Feb 2026 05:57:53 +0100 Subject: [PATCH 1/2] feat(css): add namespaced spacing variables for padding/margin/gap Introduce explicit CSS custom properties for padding, margin, and gap (sizes: xs, sm, md, lg, xl, 2xl) that derive from the existing --oui-spacing-base. Each utility gets its own namespace (--padding-, --margin-, --gap-) to avoid collisions with container-related variables. This prevents Tailwind v4 resolution issues where generic --spacing- values (e.g. --spacing-xl) could be picked up as --container-* values, ensuring predictable spacing utilities and safer future upgrades. --- .../app/assets/css/tailwind/config.css | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/apps/dashboard/app/assets/css/tailwind/config.css b/apps/dashboard/app/assets/css/tailwind/config.css index f36c4832..fa35c45b 100644 --- a/apps/dashboard/app/assets/css/tailwind/config.css +++ b/apps/dashboard/app/assets/css/tailwind/config.css @@ -113,6 +113,28 @@ --radius-4xl: calc(2rem * var(--oui-radius-base, 1)); --spacing: calc(0.25rem * var(--oui-spacing-base, 1)); + + /* Named spacing for padding/margin/gap utilities. + Uses per-utility namespaces to avoid colliding with --container-* + (Tailwind v4 resolves max-w-xl via --spacing-xl before --container-xl). */ + --padding-xs: calc(0.25rem * var(--oui-spacing-base, 1)); + --padding-sm: calc(0.5rem * var(--oui-spacing-base, 1)); + --padding-md: calc(1rem * var(--oui-spacing-base, 1)); + --padding-lg: calc(1.5rem * var(--oui-spacing-base, 1)); + --padding-xl: calc(2rem * var(--oui-spacing-base, 1)); + --padding-2xl: calc(3rem * var(--oui-spacing-base, 1)); + --margin-xs: calc(0.25rem * var(--oui-spacing-base, 1)); + --margin-sm: calc(0.5rem * var(--oui-spacing-base, 1)); + --margin-md: calc(1rem * var(--oui-spacing-base, 1)); + --margin-lg: calc(1.5rem * var(--oui-spacing-base, 1)); + --margin-xl: calc(2rem * var(--oui-spacing-base, 1)); + --margin-2xl: calc(3rem * var(--oui-spacing-base, 1)); + --gap-xs: calc(0.25rem * var(--oui-spacing-base, 1)); + --gap-sm: calc(0.5rem * var(--oui-spacing-base, 1)); + --gap-md: calc(1rem * var(--oui-spacing-base, 1)); + --gap-lg: calc(1.5rem * var(--oui-spacing-base, 1)); + --gap-xl: calc(2rem * var(--oui-spacing-base, 1)); + --gap-2xl: calc(3rem * var(--oui-spacing-base, 1)); --breakpoint-sm: 40rem; --breakpoint-md: 48rem; --breakpoint-lg: 64rem; From 7f163395f4cffd8ea84513435f9e11258a0a0dde Mon Sep 17 00:00:00 2001 From: veryCrunchy Date: Sun, 1 Feb 2026 05:58:11 +0100 Subject: [PATCH 2/2] feat(ui): use responsive cols prop and refine Responsive type Update OuiGrid usages to pass responsive column definitions via the :cols object (e.g. { sm: 1, md: 2, xl: 4 }) and standardize gap sizing. This replaces multiple old attribute combinations like cols, colsMd, colsLg, colsXl and class-based gaping with a single, consistent API. Apply these changes across several dashboard pages (invoices, billing, abuse, quotas, dns, role-bindings, organizations) to simplify layout declaration and improve readability. Additionally, expand the Oui types for Responsive to allow an explicit default key and tighten the union shape: - add documentation comment recommending use of default for base value - change Responsive to T | (Partial> & { default?: T }) Also fix a minor whitespace inconsistency in deployments page. These changes make responsive grid usage consistent, clearer to authors, and enable a more robust type for responsive props. --- apps/dashboard/app/components/oui/classMaps.ts | 10 ++++++---- apps/dashboard/app/components/oui/types.d.ts | 7 +++++-- apps/dashboard/app/pages/admin/bindings.vue | 4 ++-- apps/dashboard/app/pages/admin/quotas.vue | 2 +- apps/dashboard/app/pages/billing.vue | 2 +- apps/dashboard/app/pages/deployments/[id]/index.vue | 2 +- apps/dashboard/app/pages/deployments/index.vue | 2 -- apps/dashboard/app/pages/organizations.vue | 10 +++++----- apps/dashboard/app/pages/superadmin/abuse.vue | 2 +- apps/dashboard/app/pages/superadmin/dns.vue | 2 +- apps/dashboard/app/pages/superadmin/income.vue | 4 ++-- apps/dashboard/app/pages/superadmin/index.vue | 4 ++-- apps/dashboard/app/pages/superadmin/invoices.vue | 2 +- .../app/pages/superadmin/organizations/[orgId].vue | 2 +- .../dashboard/app/pages/superadmin/role-bindings.vue | 2 +- .../app/pages/superadmin/users/[userId].vue | 2 +- apps/dashboard/app/pages/superadmin/vps/[vpsId].vue | 12 ++++++------ .../app/pages/superadmin/webhook-events.vue | 2 +- 18 files changed, 38 insertions(+), 35 deletions(-) diff --git a/apps/dashboard/app/components/oui/classMaps.ts b/apps/dashboard/app/components/oui/classMaps.ts index 4e2374d0..3f91c8e6 100644 --- a/apps/dashboard/app/components/oui/classMaps.ts +++ b/apps/dashboard/app/components/oui/classMaps.ts @@ -225,9 +225,10 @@ export function responsiveClass( return mapped ? [mapped] : []; } - // Now TypeScript knows value is Partial> + // Object form: { default?: T; sm?: T; md?: T; lg?: T; xl?: T; "2xl"?: T } const classes: string[] = []; - const breakpointPrefix: Record = { + const breakpointPrefix: Record = { + default: "", sm: "sm:", md: "md:", lg: "lg:", @@ -237,9 +238,10 @@ export function responsiveClass( Object.entries(value).forEach(([k, candidate]) => { if (candidate === undefined) return; - const bp = k as Breakpoint; + const prefix = breakpointPrefix[k]; + if (prefix === undefined) return; const mapped = map[String(candidate)]; - if (mapped) classes.push(`${breakpointPrefix[bp]}${mapped}`); + if (mapped) classes.push(`${prefix}${mapped}`); }); return classes; diff --git a/apps/dashboard/app/components/oui/types.d.ts b/apps/dashboard/app/components/oui/types.d.ts index 3c28595e..7e6686f6 100644 --- a/apps/dashboard/app/components/oui/types.d.ts +++ b/apps/dashboard/app/components/oui/types.d.ts @@ -162,8 +162,11 @@ export type Breakpoint = "sm" | "md" | "lg" | "xl" | "2xl"; /** * Responsive - either a single value T or an object keyed by breakpoints. + * Use `default` for the base (no breakpoint prefix) value. * Example: * T - * | { sm?: T; md?: T; lg?: T; ... } + * | { default?: T; sm?: T; md?: T; lg?: T; ... } */ -export type Responsive = T | Partial>; +export type Responsive = + | T + | (Partial> & { default?: T }); diff --git a/apps/dashboard/app/pages/admin/bindings.vue b/apps/dashboard/app/pages/admin/bindings.vue index cc13e50b..d5f6a056 100644 --- a/apps/dashboard/app/pages/admin/bindings.vue +++ b/apps/dashboard/app/pages/admin/bindings.vue @@ -10,7 +10,7 @@ Basic Information - + Organization @@ -60,7 +60,7 @@ - + Resource Type
- + - + Select Organization { - const router = useRouter(); router.push(`/deployments/${id}`); }; @@ -911,7 +910,6 @@ // Navigate to the detail page to finish configuration if (deployment) { - const router = useRouter(); router.push(`/deployments/${deployment.id}`); } } catch (error) { diff --git a/apps/dashboard/app/pages/organizations.vue b/apps/dashboard/app/pages/organizations.vue index c877aab3..080e692f 100644 --- a/apps/dashboard/app/pages/organizations.vue +++ b/apps/dashboard/app/pages/organizations.vue @@ -1129,7 +1129,7 @@