mirror of
https://github.com/hunvreus/basecoat.git
synced 2026-11-15 14:38:27 +00:00
Adding mobile support for sidebar
This commit is contained in:
+116
-111
@@ -33,11 +33,12 @@
|
||||
--sidebar-accent-foreground: oklch(0.205 0 0);
|
||||
--sidebar-border: oklch(0.922 0 0);
|
||||
--sidebar-ring: oklch(0.708 0 0);
|
||||
--sidebar-width: 16rem;
|
||||
--sidebar-mobile-width: 18rem;
|
||||
--scrollbar-track: transparent;
|
||||
--scrollbar-thumb: rgba(0, 0, 0, 0.3);
|
||||
--scrollbar-width: 6px;
|
||||
--scrollbar-radius: 6px;
|
||||
--sidebar-width: 16rem;
|
||||
--chevron-down-icon: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="oklch(0.556 0 0)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down-icon lucide-chevron-down"><path d="m6 9 6 6 6-6"/></svg>'); /* --muted-foreground */
|
||||
--chevron-down-icon-50: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="oklch(0.556 0 0 / 0.5)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down-icon lucide-chevron-down"><path d="m6 9 6 6 6-6"/></svg>'); /* --muted-foreground + 50% opacity */
|
||||
--check-icon: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="oklch(0.556 0 0)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check-icon lucide-check"><path d="M20 6 9 17l-5-5"/></svg>'); /* --muted-foreground */
|
||||
@@ -75,10 +76,7 @@
|
||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||
--sidebar-border: oklch(1 0 0 / 10%);
|
||||
--sidebar-ring: oklch(0.439 0 0);
|
||||
--scrollbar-track: transparent;
|
||||
--scrollbar-thumb: rgba(255, 255, 255, 0.3);
|
||||
--scrollbar-width: 6px;
|
||||
--scrollbar-radius: 6px;
|
||||
--chevron-down-icon: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="oklch(0.708 0 0)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down-icon lucide-chevron-down"><path d="m6 9 6 6 6-6"/></svg>'); /* --muted-foreground */
|
||||
--chevron-down-icon-50: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="oklch(0.708 0 0 / 0.5)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down-icon lucide-chevron-down"><path d="m6 9 6 6 6-6"/></svg>'); /* --muted-foreground + 50% opacity */
|
||||
--check-icon: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="oklch(0.708 0 0)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check-icon lucide-check"><path d="M20 6 9 17l-5-5"/></svg>');/* --muted-foreground */
|
||||
@@ -121,8 +119,6 @@
|
||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||
--color-sidebar-border: var(--sidebar-border);
|
||||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
/* --font-sans: 'Inter var', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
--font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; */
|
||||
}
|
||||
|
||||
@layer base {
|
||||
@@ -746,6 +742,120 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Sidebar */
|
||||
.sidebar {
|
||||
&:not([aria-hidden]),
|
||||
&[aria-hidden=false] {
|
||||
@apply max-md:bg-black/50 max-md:fixed max-md:inset-0 max-md:z-40;
|
||||
}
|
||||
nav {
|
||||
@apply bg-sidebar text-sidebar-foreground flex flex-col w-(--sidebar-mobile-width) md:w-(--sidebar-width) fixed inset-y-0 transition-transform ease-in-out duration-300 z-50;
|
||||
}
|
||||
&:not([data-side]),
|
||||
&[data-side=left] {
|
||||
nav {
|
||||
@apply left-0 border-r;
|
||||
}
|
||||
& + * {
|
||||
@apply relative md:ml-(--sidebar-width) transition-[margin] ease-in-out duration-300;
|
||||
}
|
||||
&[aria-hidden=true] {
|
||||
nav {
|
||||
@apply -translate-x-full;
|
||||
}
|
||||
& + * {
|
||||
@apply md:ml-0;
|
||||
}
|
||||
}
|
||||
}
|
||||
&[data-side=right] {
|
||||
nav {
|
||||
@apply right-0 border-l;
|
||||
}
|
||||
& + * {
|
||||
@apply relative md:mr-(--sidebar-width) transition-[margin] ease-in-out duration-300;
|
||||
}
|
||||
&[aria-hidden=true] {
|
||||
nav {
|
||||
@apply translate-x-full;
|
||||
}
|
||||
& + * {
|
||||
@apply md:mr-0;
|
||||
}
|
||||
}
|
||||
}
|
||||
nav {
|
||||
> header,
|
||||
> footer {
|
||||
@apply flex flex-col gap-2 p-2;
|
||||
}
|
||||
[role=separator] {
|
||||
@apply border-sidebar-border mx-2 w-auto;
|
||||
}
|
||||
> section {
|
||||
@apply flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto;
|
||||
|
||||
> [role=group] {
|
||||
@apply relative flex w-full min-w-0 flex-col p-2;
|
||||
}
|
||||
h3 {
|
||||
@apply text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0;
|
||||
}
|
||||
ul {
|
||||
@apply flex w-full min-w-0 flex-col gap-1;
|
||||
|
||||
li {
|
||||
@apply relative;
|
||||
|
||||
> a,
|
||||
> details > summary {
|
||||
@apply flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0;
|
||||
|
||||
&:not([data-variant]),
|
||||
&[data-variant=default] {
|
||||
@apply hover:bg-sidebar-accent hover:text-sidebar-accent-foreground;
|
||||
}
|
||||
&[data-variant=outline] {
|
||||
@apply bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))];
|
||||
}
|
||||
&:not([data-size]),
|
||||
&[data-size=default] {
|
||||
@apply h-8 text-sm;
|
||||
}
|
||||
&[data-size=sm] {
|
||||
@apply h-7 text-xs;
|
||||
}
|
||||
&[data-size=lg] {
|
||||
@apply h-12 text-sm group-data-[collapsible=icon]:p-0!;
|
||||
}
|
||||
}
|
||||
> details {
|
||||
&:not([open]) {
|
||||
> summary {
|
||||
&::after {
|
||||
@apply -rotate-90;
|
||||
}
|
||||
}
|
||||
}
|
||||
> summary {
|
||||
&::after {
|
||||
@apply content-[''] block size-3.5 bg-primary ml-auto transition-transform ease-linear;
|
||||
@apply mask-[image:var(--chevron-down-icon)] mask-size-[1rem] mask-no-repeat mask-center;
|
||||
}
|
||||
}
|
||||
&::details-content {
|
||||
@apply px-3.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
ul {
|
||||
@apply border-sidebar-border flex min-w-0 translate-x-px flex-col gap-1 border-l px-2.5 py-0.5 w-full;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Switch */
|
||||
@layer components {
|
||||
.form input[type='checkbox'][role='switch'],
|
||||
@@ -909,109 +1019,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Sidebar */
|
||||
.sidebar {
|
||||
@apply bg-sidebar text-sidebar-foreground flex flex-col w-(--sidebar-width) fixed inset-y-0 transition-transform ease-in-out z-50;
|
||||
/* h-svh */
|
||||
|
||||
&:not([data-side]),
|
||||
&[data-side=left] {
|
||||
@apply left-0 border-r aria-[hidden=true]:-translate-x-full;
|
||||
|
||||
& + * {
|
||||
@apply relative ml-(--sidebar-width) transition-[margin] ease-in-out;
|
||||
}
|
||||
&[aria-hidden=true] + * {
|
||||
@apply ml-0;
|
||||
}
|
||||
}
|
||||
&[data-side=right] {
|
||||
@apply right-0 border-l aria-[hidden=true]:translate-x-full;
|
||||
|
||||
& + * {
|
||||
@apply relative mr-(--sidebar-width) transition-[margin] ease-in-out;
|
||||
}
|
||||
&[aria-hidden=true] + * {
|
||||
@apply mr-0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
> header,
|
||||
> footer {
|
||||
@apply flex flex-col gap-2 p-2;
|
||||
}
|
||||
[role=separator] {
|
||||
@apply border-sidebar-border mx-2 w-auto;
|
||||
}
|
||||
> section {
|
||||
@apply flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto;
|
||||
|
||||
> [role=group] {
|
||||
@apply relative flex w-full min-w-0 flex-col p-2;
|
||||
}
|
||||
|
||||
h3 {
|
||||
@apply text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0;
|
||||
}
|
||||
|
||||
/* GROUP CONTENT: w-full text-sm */
|
||||
ul {
|
||||
@apply flex w-full min-w-0 flex-col gap-1;
|
||||
|
||||
li {
|
||||
/* group/menu-item */
|
||||
@apply relative;
|
||||
|
||||
> a,
|
||||
> details > summary {
|
||||
/* peer/menu-button */
|
||||
@apply flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0;
|
||||
|
||||
&:not([data-variant]),
|
||||
&[data-variant=default] {
|
||||
@apply hover:bg-sidebar-accent hover:text-sidebar-accent-foreground;
|
||||
}
|
||||
&[data-variant=outline] {
|
||||
@apply bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))];
|
||||
}
|
||||
&:not([data-size]),
|
||||
&[data-size=default] {
|
||||
@apply h-8 text-sm;
|
||||
}
|
||||
&[data-size=sm] {
|
||||
@apply h-7 text-xs;
|
||||
}
|
||||
&[data-size=lg] {
|
||||
@apply h-12 text-sm group-data-[collapsible=icon]:p-0!;
|
||||
}
|
||||
}
|
||||
|
||||
> details {
|
||||
&:not([open]) {
|
||||
> summary {
|
||||
&::after {
|
||||
@apply -rotate-90;
|
||||
}
|
||||
}
|
||||
}
|
||||
> summary {
|
||||
&::after {
|
||||
@apply content-[''] block size-3.5 bg-primary ml-auto transition-transform ease-linear;
|
||||
@apply mask-[image:var(--chevron-down-icon)] mask-size-[1rem] mask-no-repeat mask-center;
|
||||
}
|
||||
}
|
||||
&::details-content {
|
||||
@apply px-3.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
@apply border-sidebar-border flex min-w-0 translate-x-px flex-col gap-1 border-l px-2.5 py-0.5 w-full;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,9 +10,9 @@
|
||||
{% macro popover(
|
||||
id=None,
|
||||
trigger=None,
|
||||
main_attrs=None,
|
||||
trigger_attrs=None,
|
||||
content_attrs=None
|
||||
main_attrs={},
|
||||
trigger_attrs={},
|
||||
content_attrs={}
|
||||
) %}
|
||||
<div
|
||||
class="popover {{ main_attrs.class }}"
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ window.basecoat.registerDialog = function(Alpine) {
|
||||
$main: {
|
||||
'@dialog:open.window'(e) { if (e.detail && e.detail.id === this.id) this.show() },
|
||||
'@dialog:close.window'(e) { if (e.detail && e.detail.id === this.id) this.hide() },
|
||||
'@keydown.escape.window'() { this.open && this.hide(); },
|
||||
'@keydown.escape.window'() { this.open && this.hide() },
|
||||
},
|
||||
$trigger: {
|
||||
'@click'() { this.show() },
|
||||
|
||||
+2
-6
@@ -14,12 +14,8 @@ window.basecoat.registerSidebar= function(Alpine) {
|
||||
$main: {
|
||||
'@sidebar:open.window'(e) { if (!this.id || (e.detail && e.detail.id === this.id)) this.open = true },
|
||||
'@sidebar:close.window'(e) { if (!this.id || (e.detail && e.detail.id === this.id)) this.open = false },
|
||||
'@sidebar:toggle.window'(e) {
|
||||
if (!this.id || (e.detail && e.detail.id === this.id)) {
|
||||
this.open = !this.open;
|
||||
console.log('toggle', e);
|
||||
}
|
||||
},
|
||||
'@sidebar:toggle.window'(e) { if (!this.id || (e.detail && e.detail.id === this.id)) this.open = !this.open },
|
||||
'@click'(e) { if (e.target === this.$el) this.open = false },
|
||||
':aria-hidden'() { return !this.open },
|
||||
':inert'() { return !this.open },
|
||||
},
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
{% macro popover(
|
||||
id=None,
|
||||
trigger=None,
|
||||
main_attrs=None,
|
||||
trigger_attrs=None,
|
||||
content_attrs=None
|
||||
main_attrs={},
|
||||
trigger_attrs={},
|
||||
content_attrs={}
|
||||
) %}
|
||||
<div
|
||||
class="popover {{ main_attrs.class }}"
|
||||
|
||||
+59
-46
@@ -4,7 +4,13 @@
|
||||
{% set item_id = parent_id ~ loop.index %}
|
||||
{% if item.type == 'item' %}
|
||||
<li>
|
||||
<a href="{{ item.url }}" {{ 'aria-current="page"' if item.current }}>
|
||||
<a
|
||||
href="{{ item.url }}"
|
||||
{{ 'aria-current="page"' if item.current }}
|
||||
{% for key, value in item.attrs %}
|
||||
{% if key != 'href' and key != 'aria-current' %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
>
|
||||
{% if item.icon %}{{ item.icon | safe }}{% endif %}
|
||||
<span>{{ item.label }}</span>
|
||||
</a>
|
||||
@@ -12,7 +18,12 @@
|
||||
{% elif item.type == 'submenu' %}
|
||||
<li>
|
||||
<details {{ 'open' if item.open }} id="submenu-{{ item_id }}">
|
||||
<summary aria-controls="submenu-{{ item_id }}-content">
|
||||
<summary
|
||||
aria-controls="submenu-{{ item_id }}-content"
|
||||
{% for key, value in item.attrs %}
|
||||
{% if key != 'aria-controls' %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
>
|
||||
{% if item.icon %}{{ item.icon | safe }}{% endif %}
|
||||
{{ item.label }}
|
||||
</summary>
|
||||
@@ -40,10 +51,9 @@
|
||||
footer_attrs={},
|
||||
content_wrapper_attrs=None
|
||||
) %}
|
||||
<nav
|
||||
<div
|
||||
{% if id %}id="{{ id }}"{% endif %}
|
||||
class="sidebar peer {{ main_attrs.class }}"
|
||||
aria-label="{{ label }}"
|
||||
class="sidebar {{ main_attrs.class }}"
|
||||
aria-hidden="{{ 'false' if open else 'true' }}"
|
||||
data-side="{{ side if side else 'left' }}"
|
||||
{{ 'inert' if not open }}
|
||||
@@ -53,49 +63,52 @@
|
||||
x-data="sidebar({{ 'true' if open else 'false' }})"
|
||||
x-bind="$main"
|
||||
>
|
||||
{% if header %}
|
||||
<header
|
||||
{% for key, value in header_attrs %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
<nav
|
||||
aria-label="{{ label }}"
|
||||
>
|
||||
{{ header | safe }}
|
||||
</header>
|
||||
{% endif %}
|
||||
<hr role="separator"/>
|
||||
|
||||
<section
|
||||
{% for key, value in content_attrs %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
{% if menu %}
|
||||
{% for group_or_item in menu %}
|
||||
{% set group_id = loop.index %}
|
||||
{% if group_or_item.type == 'group' %}
|
||||
<div role="group" aria-labelledby="nav-group-{{ group_id }}">
|
||||
<h3 id="nav-group-{{ group_id }}">{{ group_or_item.label }}</h3>
|
||||
{{ render_menu_items(group_or_item.items, 0, group_id + '-') }}
|
||||
</div>
|
||||
{% elif group_or_item.type == 'separator' %}
|
||||
<hr role="separator"/>
|
||||
{% elif group_or_item.type == 'item' or group_or_item.type == 'submenu' %}
|
||||
{{ render_menu_items([group_or_item], 0, 'top-' + group_id + '-') }}
|
||||
{% endif %}
|
||||
{% if header %}
|
||||
<header
|
||||
{% for key, value in header_attrs %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{{ caller() if caller }}
|
||||
>
|
||||
{{ header | safe }}
|
||||
</header>
|
||||
{% endif %}
|
||||
</section>
|
||||
|
||||
{% if footer %}
|
||||
<footer
|
||||
{% for key, value in footer_attrs %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
{{ footer | safe }}
|
||||
</footer>
|
||||
{% endif %}
|
||||
</nav>
|
||||
<section
|
||||
{% for key, value in content_attrs %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
{% if menu %}
|
||||
{% for group_or_item in menu %}
|
||||
{% set group_id = loop.index %}
|
||||
{% if group_or_item.type == 'group' %}
|
||||
<div role="group" aria-labelledby="nav-group-{{ group_id }}">
|
||||
<h3 id="nav-group-{{ group_id }}">{{ group_or_item.label }}</h3>
|
||||
{{ render_menu_items(group_or_item.items, 0, group_id + '-') }}
|
||||
</div>
|
||||
{% elif group_or_item.type == 'separator' %}
|
||||
<hr role="separator"/>
|
||||
{% elif group_or_item.type == 'item' or group_or_item.type == 'submenu' %}
|
||||
{{ render_menu_items([group_or_item], 0, 'top-' + group_id + '-') }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{{ caller() if caller }}
|
||||
{% endif %}
|
||||
</section>
|
||||
|
||||
{% if footer %}
|
||||
<footer
|
||||
{% for key, value in footer_attrs %}
|
||||
{{ key }}="{{ value }}"
|
||||
{% endfor %}
|
||||
>
|
||||
{{ footer | safe }}
|
||||
</footer>
|
||||
{% endif %}
|
||||
</nav>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
Reference in New Issue
Block a user