refactor(z-index): introduce a shared named z-index scale (#723)

* refactor(z-index): introduce a shared named z-index scale

Replace scattered magic z-index numbers (100, 150, 200, 201, 250) with
named layers (z-app-bar, z-app-bar-elevated, z-modal, z-menu-elevated)
so intent is documented at each call site and future additions insert
predictably instead of colliding.

Defined via Tailwind's @utility directive in two places that mirror
each other: packages/ui/src/index.css (ui:-prefixed, for components
that live in the @cio/ui package) and apps/dashboard/src/app.css
(unprefixed, for dashboard-only files) — a ui:-prefixed custom utility
only compiles if it is also used somewhere under packages/ui/src, so
dashboard-only usages need the unprefixed twin.

Also fixes two pre-existing dropdown/select menus (tags-admin.svelte,
mark-exercise-modal.svelte) that used ui:z-[250], which never actually
compiled to any CSS rule since nothing under packages/ui/src used that
exact class.

Co-authored-by: Rotimi Best <[email protected]>

* fix(ui): safelist the packaged z-index scale so it always compiles

@utility only emits a class when Tailwind's scanner sees that exact
candidate under packages/ui/src, so ui:z-app-bar-elevated and
ui:z-menu-elevated were documented but silently missing from the
compiled @cio/ui CSS since nothing inside the package itself used
them. Safelist all four layers via @source inline() so the shipped
scale always matches what's documented, regardless of which layers
this package happens to reference internally.

Co-authored-by: Rotimi Best <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Rotimi Best <[email protected]>
This commit is contained in:
Rotimi Best
2026-07-11 08:25:11 +01:00
committed by GitHub
co-authored by Rotimi Best Cursor Agent
parent c0f82d9573
commit 0836d9b5bb
13 changed files with 83 additions and 14 deletions
+22
View File
@@ -8,6 +8,28 @@
@custom-variant dark (&:where(.dark, .dark *));
/**
* Shared z-index scale, mirrored from `packages/ui/src/index.css`.
*
* Dashboard code has its own (unprefixed) Tailwind pipeline, separate from `@cio/ui`'s
* `ui:`-prefixed one — a `ui:`-prefixed utility only compiles if that exact class string is
* also used somewhere under `packages/ui/src`. So dashboard-only usages (page headers, the
* course sidebar, dropdowns/selects that need to escape a sticky header or a modal) must use
* these unprefixed names instead. Keep the two scales numerically in sync; see
* `packages/ui/README.md` for the full write-up of each layer.
*/
@utility z-app-bar {
z-index: 100;
}
@utility z-app-bar-elevated {
z-index: 150;
}
@utility z-menu-elevated {
z-index: 250;
}
/* Geist variable font — self-hosted */
@font-face {
font-family: 'Geist';
@@ -5,7 +5,7 @@
</script>
<header
class="border-border ui:bg-background sticky top-0 z-100 flex h-12 w-full shrink-0 items-center gap-2 border-b backdrop-blur transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-8"
class="border-border ui:bg-background z-app-bar sticky top-0 flex h-12 w-full shrink-0 items-center gap-2 border-b backdrop-blur transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-8"
>
<div class="flex w-full items-center gap-2 px-4">
<Sidebar.Trigger />
@@ -58,7 +58,7 @@
</script>
<header
class="border-border ui:bg-background sticky top-0 z-100 flex h-12 w-full shrink-0 items-center gap-2 border-b backdrop-blur transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-8"
class="border-border ui:bg-background z-app-bar sticky top-0 flex h-12 w-full shrink-0 items-center gap-2 border-b backdrop-blur transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-8"
>
<div class="flex w-full items-center gap-2 px-4">
<Sidebar.Trigger />
@@ -296,7 +296,7 @@
>
<EllipsisVerticalIcon class="h-5 w-5" />
</DropdownMenu.Trigger>
<DropdownMenu.Content align="end" class="ui:z-[250]">
<DropdownMenu.Content align="end" class="z-menu-elevated">
<DropdownMenu.Item
class="text-red-600"
onclick={() => {
@@ -366,7 +366,7 @@
<Select.Trigger class="w-full">
{status.text}
</Select.Trigger>
<Select.Content class="ui:z-[250]">
<Select.Content class="z-menu-elevated">
{#each SELECTABLE_STATUS as statusOption}
<Select.Item value={statusOption.id.toString()}>
{statusOption.text}
@@ -112,7 +112,7 @@
{#if !isOrgLoaded}
<SidebarSkeleton />
{:else}
<Sidebar.Root collapsible="icon" class="ui:z-150!" mobileOverlayClass="ui:z-150!">
<Sidebar.Root collapsible="icon" class="z-app-bar-elevated!" mobileOverlayClass="z-app-bar-elevated!">
<Sidebar.Header>
<CourseSidebarLogo />
</Sidebar.Header>
@@ -405,7 +405,7 @@
}
</script>
<Page.Header isSticky={true} class="top-12! z-100! min-h-[36px]">
<Page.Header isSticky={true} class="z-app-bar! top-12! min-h-[36px]">
<Page.HeaderContent>
<Page.Title class="flex flex-col gap-2">
<span>{exerciseDisplayTitle}</span>
@@ -451,7 +451,7 @@
</Button>
{/snippet}
</DropdownMenu.Trigger>
<DropdownMenu.Content align="end" class="z-201!">
<DropdownMenu.Content align="end" class="z-menu-elevated!">
<DropdownMenu.Item onclick={addSectionFromHeader}>
{$t('course.navItem.lessons.exercises.all_exercises.add_section')}
</DropdownMenu.Item>
@@ -490,7 +490,7 @@
</Button>
{/snippet}
</DropdownMenu.Trigger>
<DropdownMenu.Content align="end" class="z-201!">
<DropdownMenu.Content align="end" class="z-menu-elevated!">
{#if hasSections($questionnaire.sections)}
<DropdownMenu.Item onclick={() => (reorderQuestions = !reorderQuestions)}>
{reorderQuestions
@@ -148,7 +148,7 @@
<div class="hidden shrink-0 md:block md:w-(--side-panel-width)"></div>
<aside
class="ui:bg-background fixed inset-y-0 right-0 z-100 flex h-screen w-full flex-col border-l md:w-(--side-panel-width)"
class="ui:bg-background z-app-bar fixed inset-y-0 right-0 flex h-screen w-full flex-col border-l md:w-(--side-panel-width)"
aria-label={t.get(def.titleKey)}
>
<button
@@ -383,7 +383,7 @@
: $t('tags_admin.tag_modal.select_category')}
</p>
</Select.Trigger>
<Select.Content class="ui:z-[250]">
<Select.Content class="z-menu-elevated">
{#each tagApi.tagGroups as group (group.id)}
<Select.Item value={group.id}>{group.name}</Select.Item>
{/each}