refactor: migrate and consolidate UI templates to compile-time Askama component macros
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
{% extends "base.html" %}
|
||||
{% import "components/macros.html" as ui %}
|
||||
|
||||
{% block title %}Buttons - Design System Wiki{% endblock %}
|
||||
|
||||
@@ -33,82 +34,44 @@
|
||||
|
||||
<!-- Demo Viewport -->
|
||||
<div id="btn-sandbox" class="wiki-pane flex flex-wrap gap-4 py-2">
|
||||
<button class="inline-flex items-center justify-center rounded-xl text-xs font-bold transition-all focus:outline-none focus:ring-2 focus:ring-sky-500 focus:ring-offset-2 focus:ring-offset-background bg-primary text-primary-foreground hover:opacity-90 px-4 py-2.5 shadow-md shadow-slate-950/20 active:scale-95">
|
||||
Primary
|
||||
</button>
|
||||
|
||||
<button class="inline-flex items-center justify-center rounded-xl text-xs font-bold transition-all focus:outline-none focus:ring-2 focus:ring-sky-500 focus:ring-offset-2 focus:ring-offset-background bg-secondary text-secondary-foreground hover:bg-secondary/80 px-4 py-2.5 active:scale-95">
|
||||
Secondary
|
||||
</button>
|
||||
|
||||
<button class="inline-flex items-center justify-center rounded-xl text-xs font-bold transition-all focus:outline-none focus:ring-2 focus:ring-sky-500 focus:ring-offset-2 focus:ring-offset-background border border-border bg-transparent hover:bg-secondary text-slate-200 px-4 py-2.5 active:scale-95">
|
||||
Outline
|
||||
</button>
|
||||
|
||||
<button class="inline-flex items-center justify-center rounded-xl text-xs font-bold transition-all focus:outline-none focus:ring-2 focus:ring-rose-500 focus:ring-offset-2 focus:ring-offset-background bg-destructive text-destructive-foreground hover:opacity-90 px-4 py-2.5 shadow-md active:scale-95">
|
||||
Destructive
|
||||
</button>
|
||||
{{ ui::button(label="Primary", variant="primary") }}
|
||||
{{ ui::button(label="Secondary", variant="secondary") }}
|
||||
{{ ui::button(label="Outline", variant="outline") }}
|
||||
{{ ui::button(label="Destructive", variant="destructive") }}
|
||||
|
||||
<!-- Icon Button -->
|
||||
<button class="inline-flex items-center gap-2 justify-center rounded-xl text-xs font-bold transition-all focus:outline-none focus:ring-2 focus:ring-sky-500 focus:ring-offset-2 focus:ring-offset-background bg-indigo-600 hover:bg-indigo-500 text-white px-4 py-2.5 active:scale-95">
|
||||
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4v16m8-8H4"/>
|
||||
</svg>
|
||||
Create Task
|
||||
</button>
|
||||
<!-- Icon Button using safe SVG markup inside label -->
|
||||
{{ ui::button(label="<svg class='w-4 h-4' fill='none' viewBox='0 0 24 24' stroke='currentColor' stroke-width='2'><path stroke-linecap='round' stroke-linejoin='round' d='M12 4v16m8-8H4'/></svg> Create Task", variant="indigo", extra_class="gap-2") }}
|
||||
|
||||
<!-- Loading State -->
|
||||
<button disabled class="inline-flex items-center gap-2 justify-center rounded-xl text-xs font-bold bg-secondary text-slate-500 cursor-not-allowed px-4 py-2.5">
|
||||
<svg class="animate-spin h-3.5 w-3.5 text-slate-500" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
Processing...
|
||||
</button>
|
||||
<!-- Loading / Disabled state -->
|
||||
{{ ui::button(label="<svg class='animate-spin h-3.5 w-3.5 text-slate-500' fill='none' viewBox='0 0 24 24'><circle class='opacity-25' cx='12' cy='12' r='10' stroke='currentColor' stroke-width='4'></circle><path class='opacity-75' fill='currentColor' d='M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z'></path></svg> Processing...", variant="secondary", disabled=true, extra_class="gap-2 text-slate-500 cursor-not-allowed") }}
|
||||
</div>
|
||||
|
||||
<!-- Code Snippet Area -->
|
||||
<div id="btn-code" class="wiki-pane hidden space-y-4">
|
||||
<div class="relative group">
|
||||
<button class="absolute top-2 right-2 p-1.5 rounded-lg border border-border bg-popover/80 backdrop-blur text-[10px] font-semibold text-muted-foreground/90 hover:text-white hover:bg-secondary opacity-0 group-hover:opacity-100 transition-opacity duration-200 flex items-center gap-1.5" onclick="copyCodeSnippet(this)">
|
||||
<svg class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"/></svg>
|
||||
<svg class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 002 2h2a2 2 0 002-2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"/></svg>
|
||||
Copy Code
|
||||
</button>
|
||||
<pre class="bg-popover p-4 rounded-xl border border-border overflow-x-auto text-[10px] text-sky-400 font-mono"><code><!-- Primary Button -->
|
||||
<button class="inline-flex items-center justify-center rounded-xl text-xs font-bold transition-all focus:outline-none focus:ring-2 focus:ring-sky-500 focus:ring-offset-2 focus:ring-offset-background bg-primary text-primary-foreground hover:opacity-90 px-4 py-2.5 shadow-md shadow-slate-950/20 active:scale-95">
|
||||
Primary
|
||||
</button>
|
||||
<pre class="bg-popover p-4 rounded-xl border border-border overflow-x-auto text-[10px] text-sky-400 font-mono"><code>{{ "{%" }} import "components/macros.html" as ui {{ "%}" }}
|
||||
|
||||
<!-- Primary Button -->
|
||||
{{ "{{" }} ui::button(label="Primary", variant="primary") {{ "}}" }}
|
||||
|
||||
<!-- Secondary Button -->
|
||||
<button class="inline-flex items-center justify-center rounded-xl text-xs font-bold transition-all focus:outline-none focus:ring-2 focus:ring-sky-500 bg-secondary text-secondary-foreground hover:bg-secondary/80 px-4 py-2.5 active:scale-95">
|
||||
Secondary
|
||||
</button>
|
||||
{{ "{{" }} ui::button(label="Secondary", variant="secondary") {{ "}}" }}
|
||||
|
||||
<!-- Outline Button -->
|
||||
<button class="inline-flex items-center justify-center rounded-xl text-xs font-bold transition-all focus:outline-none focus:ring-2 focus:ring-sky-500 border border-border bg-transparent hover:bg-secondary text-slate-200 px-4 py-2.5 active:scale-95">
|
||||
Outline
|
||||
</button>
|
||||
{{ "{{" }} ui::button(label="Outline", variant="outline") {{ "}}" }}
|
||||
|
||||
<!-- Destructive Button -->
|
||||
<button class="inline-flex items-center justify-center rounded-xl text-xs font-bold transition-all focus:outline-none focus:ring-2 focus:ring-rose-500 bg-destructive text-destructive-foreground hover:opacity-90 px-4 py-2.5 active:scale-95">
|
||||
Destructive
|
||||
</button>
|
||||
{{ "{{" }} ui::button(label="Destructive", variant="destructive") {{ "}}" }}
|
||||
|
||||
<!-- Create Button (Icon + Text) -->
|
||||
<button class="inline-flex items-center gap-2 justify-center rounded-xl text-xs font-bold transition-all focus:outline-none focus:ring-2 focus:ring-sky-500 bg-indigo-650 hover:bg-indigo-600 text-white px-4 py-2.5 active:scale-95">
|
||||
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4v16m8-8H4"/>
|
||||
</svg>
|
||||
Create Task
|
||||
</button>
|
||||
{{ "{{" }} ui::button(label="<svg class='w-4 h-4' fill='none' viewBox='0 0 24 24' stroke='currentColor' stroke-width='2'><path stroke-linecap='round' stroke-linejoin='round' d='M12 4v16m8-8H4'/></svg> Create Task", variant="indigo", extra_class="gap-2") {{ "}}" }}
|
||||
|
||||
<!-- Spinner Loading Button -->
|
||||
<button disabled class="inline-flex items-center gap-2 justify-center rounded-xl text-xs font-bold bg-secondary text-slate-500 cursor-not-allowed px-4 py-2.5">
|
||||
<svg class="animate-spin h-3.5 w-3.5" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></svg>
|
||||
Processing...
|
||||
</button></code></pre>
|
||||
{{ "{{" }} ui::button(label="<svg class='animate-spin h-3.5 w-3.5 text-slate-500' fill='none' viewBox='0 0 24 24'><circle class='opacity-25' cx='12' cy='12' r='10' stroke='currentColor' stroke-width='4'></circle><path class='opacity-75' fill='currentColor' d='M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z'></path></svg> Processing...", variant="secondary", disabled=true, extra_class="gap-2 text-slate-500 cursor-not-allowed") {{ "}}" }}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends "base.html" %}
|
||||
{% import "components/macros.html" as ui %}
|
||||
|
||||
{% block title %}Autocomplete (Combobox) - Design System Wiki{% endblock %}
|
||||
|
||||
@@ -56,14 +57,7 @@
|
||||
<span class="text-[10px] font-bold text-emerald-400 uppercase tracking-wider block mb-1">Server-Side HTMX Search</span>
|
||||
|
||||
{% if authenticated %}
|
||||
<div class="autocomplete-combobox relative">
|
||||
<label class="block text-xs font-semibold text-muted-foreground mb-1.5">Live MongoDB Query</label>
|
||||
<input type="hidden" name="assignee_id" class="combobox-value">
|
||||
<input type="text" name="q" placeholder="Query developers database..." autocomplete="off"
|
||||
class="combobox-input block w-full px-4 py-2 bg-background border border-border text-white rounded-xl text-sm focus:outline-none focus:ring-2 focus:ring-sky-500"
|
||||
hx-get="/developers/search" hx-trigger="input changed delay:200ms" hx-target="next .combobox-results">
|
||||
<div class="combobox-results absolute z-10 w-full mt-2 bg-popover border border-border rounded-xl p-1 shadow-xl hidden"></div>
|
||||
</div>
|
||||
{{ ui::search_combobox(name="assignee_id", label="Live MongoDB Query", placeholder="Query developers database...", search_url="/developers/search", input_id="wiki-db-search", value_id="wiki-db-value") }}
|
||||
{% else %}
|
||||
<div class="rounded-2xl border border-border bg-[#09090b]/40 p-4 space-y-2 text-xs">
|
||||
<div class="flex items-center gap-1.5 text-amber-500 font-bold">
|
||||
@@ -115,19 +109,10 @@
|
||||
<svg class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 002 2h2a2 2 0 002-2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"/></svg>
|
||||
Copy Code
|
||||
</button>
|
||||
<pre class="bg-popover p-4 rounded-xl border border-border overflow-x-auto text-[10px] text-sky-400 font-mono"><code><!-- Server-Side Combobox Container (Asynchronous Query via HTMX) -->
|
||||
<div class="autocomplete-combobox relative">
|
||||
<!-- Holds the final value submitted to forms -->
|
||||
<input type="hidden" name="assignee_id" class="combobox-value">
|
||||
|
||||
<!-- Input box triggers search query. Submits with parameter 'q' -->
|
||||
<input type="text" name="q" placeholder="Search developers..." autocomplete="off"
|
||||
class="combobox-input block w-full px-4 py-2 bg-background border border-border rounded-xl text-sm focus:ring-2 focus:ring-sky-500"
|
||||
hx-get="/developers/search" hx-trigger="input changed delay:250ms" hx-target="next .combobox-results">
|
||||
|
||||
<!-- Dropdown container receives swapped HTML markup from server -->
|
||||
<div class="combobox-results absolute z-10 w-full mt-2 bg-popover border border-border rounded-xl p-1 shadow-xl hidden"></div>
|
||||
</div></code></pre>
|
||||
<pre class="bg-popover p-4 rounded-xl border border-border overflow-x-auto text-[10px] text-sky-400 font-mono"><code>{{ "{%" }} import "components/macros.html" as ui {{ "%}" }}
|
||||
|
||||
<!-- Server-Side Combobox using macro (Asynchronous Query via HTMX) -->
|
||||
{{ "{{" }} ui::search_combobox(name="assignee_id", label="Live MongoDB Query", placeholder="Query developers database...", search_url="/developers/search", input_id="wiki-db-search", value_id="wiki-db-value") {{ "}}" }}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends "base.html" %}
|
||||
{% import "components/macros.html" as ui %}
|
||||
|
||||
{% block title %}Date & Time Pickers - Design System Wiki{% endblock %}
|
||||
|
||||
@@ -33,183 +34,42 @@
|
||||
|
||||
<!-- Demo Viewport -->
|
||||
<div id="picker-sandbox" class="wiki-pane grid grid-cols-1 sm:grid-cols-2 gap-6 max-w-lg py-2">
|
||||
<!-- Date Picker -->
|
||||
<div class="space-y-2">
|
||||
<label class="block text-xs font-semibold text-slate-405">Date Picker</label>
|
||||
<div class="custom-datepicker relative inline-block w-full" id="wiki-datepicker" data-year="2026" data-month="4">
|
||||
<input type="hidden" name="wiki_date" class="datepicker-value" value="2026-05-30">
|
||||
<button type="button" class="datepicker-trigger flex h-10 w-full items-center justify-between rounded-xl border border-border bg-background px-4 py-2 text-sm text-slate-200 hover:bg-secondary transition focus:outline-none focus:ring-2 focus:ring-sky-500">
|
||||
<span class="datepicker-label flex items-center gap-2">
|
||||
<svg class="h-4 w-4 text-muted-foreground" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
|
||||
</svg>
|
||||
<span class="datepicker-text">May 30, 2026</span>
|
||||
</span>
|
||||
<svg class="h-4 w-4 text-slate-500" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><polyline points="6 9 12 15 18 9"/></svg>
|
||||
</button>
|
||||
<div class="datepicker-popover absolute left-0 z-20 mt-2 w-[270px] p-3 rounded-2xl border border-border bg-popover shadow-2xl animate-in fade-in slide-in-from-top-2 duration-200 hidden">
|
||||
<div class="flex items-center justify-between mb-3.5">
|
||||
<button type="button" class="datepicker-prev p-1.5 rounded-lg hover:bg-secondary text-muted-foreground/90 hover:text-white transition">
|
||||
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><polyline points="15 18 9 12 15 6"/></svg>
|
||||
</button>
|
||||
<span class="datepicker-month-year text-xs font-bold text-slate-200">May 2026</span>
|
||||
<button type="button" class="datepicker-next p-1.5 rounded-lg hover:bg-secondary text-muted-foreground/90 hover:text-white transition">
|
||||
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><polyline points="9 18 15 12 9 6"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="grid grid-cols-7 gap-1 text-center text-[10px] font-bold text-slate-500 mb-2">
|
||||
<span>Su</span><span>Mo</span><span>Tu</span><span>We</span><span>Th</span><span>Fr</span><span>Sa</span>
|
||||
</div>
|
||||
<div class="datepicker-days grid grid-cols-7 gap-1 text-center"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Date Picker using macro -->
|
||||
{{ ui::datepicker(id="wiki-datepicker", name="wiki_date", label="Date Picker", value="2026-05-30", display_text="May 30, 2026", data_year="2026", data_month="4") }}
|
||||
|
||||
<!-- Time Picker -->
|
||||
<div class="space-y-2">
|
||||
<label class="block text-xs font-semibold text-slate-405">Time Picker</label>
|
||||
<div class="custom-timepicker relative inline-block w-full" id="wiki-timepicker">
|
||||
<input type="hidden" name="wiki_time" class="timepicker-value" value="12:00 PM">
|
||||
<button type="button" class="timepicker-trigger flex h-10 w-full items-center justify-between rounded-xl border border-border bg-background px-4 py-2 text-sm text-slate-200 hover:bg-secondary transition focus:outline-none focus:ring-2 focus:ring-sky-500">
|
||||
<span class="timepicker-label flex items-center gap-2">
|
||||
<svg class="h-4 w-4 text-muted-foreground" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
<span class="timepicker-text">12:00 PM</span>
|
||||
</span>
|
||||
<svg class="h-4 w-4 text-slate-500" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><polyline points="6 9 12 15 18 9"/></svg>
|
||||
</button>
|
||||
<div class="timepicker-popover absolute right-0 sm:left-0 z-20 mt-2 w-[230px] p-3 rounded-2xl border border-border bg-popover shadow-2xl animate-in fade-in slide-in-from-top-2 duration-200 hidden">
|
||||
<div class="flex gap-2 justify-center items-center">
|
||||
<div class="flex flex-col items-center">
|
||||
<span class="text-[9px] font-bold text-slate-500 mb-1.5 uppercase tracking-wider">Hr</span>
|
||||
<div class="h-32 overflow-y-auto w-12 text-center rounded-lg border border-border bg-popover scrollbar-none timepicker-col-hours"></div>
|
||||
</div>
|
||||
<span class="text-slate-500 font-bold self-end mb-12">:</span>
|
||||
<div class="flex flex-col items-center">
|
||||
<span class="text-[9px] font-bold text-slate-500 mb-1.5 uppercase tracking-wider">Min</span>
|
||||
<div class="h-32 overflow-y-auto w-12 text-center rounded-lg border border-border bg-popover scrollbar-none timepicker-col-minutes"></div>
|
||||
</div>
|
||||
<div class="flex flex-col items-center ml-1">
|
||||
<span class="text-[9px] font-bold text-slate-500 mb-1.5 uppercase tracking-wider">Am/Pm</span>
|
||||
<div class="flex flex-col gap-1 w-12">
|
||||
<button type="button" class="timepicker-ampm-btn py-1.5 rounded-lg text-xs font-bold hover:bg-secondary transition text-muted-foreground">AM</button>
|
||||
<button type="button" class="timepicker-ampm-btn py-1.5 rounded-lg text-xs font-bold hover:bg-secondary transition text-muted-foreground">PM</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Time Picker using macro -->
|
||||
{{ ui::timepicker(id="wiki-timepicker", name="wiki_time", label="Time Picker", value="12:00 PM") }}
|
||||
</div>
|
||||
|
||||
<!-- Code Snippet Area -->
|
||||
<div id="picker-code" class="wiki-pane hidden space-y-4">
|
||||
<!-- Date Picker Code -->
|
||||
<!-- Date Picker Macro -->
|
||||
<div class="space-y-2">
|
||||
<span class="text-xs font-bold text-muted-foreground block">Date Picker HTML Structure</span>
|
||||
<span class="text-xs font-bold text-muted-foreground block">Date Picker Macro</span>
|
||||
<div class="relative group">
|
||||
<button class="absolute top-2 right-2 p-1.5 rounded-lg border border-border bg-popover/80 backdrop-blur text-[10px] font-semibold text-muted-foreground/90 hover:text-white hover:bg-secondary opacity-0 group-hover:opacity-100 transition-opacity duration-200 flex items-center gap-1.5" onclick="copyCodeSnippet(this)">
|
||||
<svg class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 002 2h2a2 2 0 002-2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"/></svg>
|
||||
Copy Date Picker Code
|
||||
</button>
|
||||
<pre class="bg-popover p-4 rounded-xl border border-border overflow-x-auto text-[10px] text-sky-400 font-mono"><code><!-- Custom Date Picker Container
|
||||
- class "custom-datepicker": required for JavaScript target binding
|
||||
- data-year / data-month: initializes the calendar viewport (month is 0-indexed: 4 = May) -->
|
||||
<div class="custom-datepicker relative inline-block w-full" id="unique-datepicker-id" data-year="2026" data-month="4">
|
||||
<!-- Hidden input that holds the actual selected date (YYYY-MM-DD) to submit with the form -->
|
||||
<input type="hidden" name="date_value" class="datepicker-value" value="2026-05-30">
|
||||
|
||||
<!-- Trigger Button: opens/closes the dropdown calendar popover -->
|
||||
<button type="button" class="datepicker-trigger flex h-10 w-full items-center justify-between rounded-xl border border-border bg-background px-4 py-2 text-sm text-slate-200">
|
||||
<span class="datepicker-label flex items-center gap-2">
|
||||
<svg class="h-4 w-4 text-muted-foreground" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
|
||||
</svg>
|
||||
<!-- text-label: will be dynamically updated by components.js when a day is selected -->
|
||||
<span class="datepicker-text">Pick a date</span>
|
||||
</span>
|
||||
<svg class="h-4 w-4 text-slate-500" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><polyline points="6 9 12 15 18 9"/></svg>
|
||||
</button>
|
||||
<pre class="bg-popover p-4 rounded-xl border border-border overflow-x-auto text-[10px] text-sky-400 font-mono"><code>{{ "{%" }} import "components/macros.html" as ui {{ "%}" }}
|
||||
|
||||
<!-- Popover: holds month controls and calendar grids -->
|
||||
<div class="datepicker-popover absolute left-0 z-20 mt-2 w-[270px] p-3 rounded-2xl border border-border bg-popover shadow-2xl hidden">
|
||||
<!-- Navigation Controls -->
|
||||
<div class="flex items-center justify-between mb-3.5">
|
||||
<button type="button" class="datepicker-prev p-1.5 rounded-lg hover:bg-secondary text-muted-foreground/90 hover:text-white">
|
||||
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><polyline points="15 18 9 12 15 6"/></svg>
|
||||
</button>
|
||||
<!-- Current visible Month/Year label -->
|
||||
<span class="datepicker-month-year text-xs font-bold text-slate-200"></span>
|
||||
<button type="button" class="datepicker-next p-1.5 rounded-lg hover:bg-secondary text-muted-foreground/90 hover:text-white">
|
||||
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><polyline points="9 18 15 12 9 6"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<!-- Weekday column labels -->
|
||||
<div class="grid grid-cols-7 gap-1 text-center text-[10px] font-bold text-slate-500 mb-2">
|
||||
<span>Su</span><span>Mo</span><span>Tu</span><span>We</span><span>Th</span><span>Fr</span><span>Sa</span>
|
||||
</div>
|
||||
<!-- Day Grid (filled dynamically with days by components.js on load) -->
|
||||
<div class="datepicker-days grid grid-cols-7 gap-1 text-center"></div>
|
||||
</div>
|
||||
</div></code></pre>
|
||||
<!-- Custom Date Picker using macro -->
|
||||
{{ "{{" }} ui::datepicker(id="my-datepicker", name="my_date", label="Date Picker", value="2026-05-30", display_text="May 30, 2026", data_year="2026", data_month="4") {{ "}}" }}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Time Picker Code -->
|
||||
<!-- Time Picker Macro -->
|
||||
<div class="space-y-2">
|
||||
<span class="text-xs font-bold text-muted-foreground block">Time Picker HTML Structure</span>
|
||||
<span class="text-xs font-bold text-muted-foreground block">Time Picker Macro</span>
|
||||
<div class="relative group">
|
||||
<button class="absolute top-2 right-2 p-1.5 rounded-lg border border-border bg-popover/80 backdrop-blur text-[10px] font-semibold text-slate-455 hover:text-white hover:bg-secondary opacity-0 group-hover:opacity-100 transition-opacity duration-200 flex items-center gap-1.5" onclick="copyCodeSnippet(this)">
|
||||
<svg class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 002 2h2a2 2 0 002-2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"/></svg>
|
||||
Copy Time Picker Code
|
||||
</button>
|
||||
<pre class="bg-popover p-4 rounded-xl border border-border overflow-x-auto text-[10px] text-sky-400 font-mono"><code><!-- Custom Time Picker Container
|
||||
- class "custom-timepicker": required for JavaScript target binding -->
|
||||
<div class="custom-timepicker relative inline-block w-full" id="unique-timepicker-id">
|
||||
<!-- Hidden input holds selected value (e.g., "12:00 PM") for form submission -->
|
||||
<input type="hidden" name="time_value" class="timepicker-value" value="12:00 PM">
|
||||
|
||||
<!-- Trigger Button -->
|
||||
<button type="button" class="timepicker-trigger flex h-10 w-full items-center justify-between rounded-xl border border-border bg-background px-4 py-2 text-sm text-slate-200">
|
||||
<span class="timepicker-label flex items-center gap-2">
|
||||
<svg class="h-4 w-4 text-muted-foreground" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
<!-- Time text label updated dynamically on pick -->
|
||||
<span class="timepicker-text">12:00 PM</span>
|
||||
</span>
|
||||
<svg class="h-4 w-4 text-slate-500" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><polyline points="6 9 12 15 18 9"/></svg>
|
||||
</button>
|
||||
|
||||
<!-- Time Picker Dropdown Menu -->
|
||||
<div class="timepicker-popover absolute left-0 z-20 mt-2 w-[230px] p-3 rounded-2xl border border-border bg-popover shadow-2xl hidden">
|
||||
<div class="flex gap-2 justify-center items-center">
|
||||
<!-- Hours Column (filled with <button>s 1 to 12 dynamically by components.js) -->
|
||||
<div class="flex flex-col items-center">
|
||||
<span class="text-[9px] font-bold text-slate-500 mb-1.5 uppercase tracking-wider">Hr</span>
|
||||
<div class="h-32 overflow-y-auto w-12 text-center rounded-lg border border-border bg-popover scrollbar-none timepicker-col-hours"></div>
|
||||
</div>
|
||||
|
||||
<span class="text-slate-500 font-bold self-end mb-12">:</span>
|
||||
|
||||
<!-- Minutes Column (filled with <button>s 00 to 55 in 5m steps dynamically) -->
|
||||
<div class="flex flex-col items-center">
|
||||
<span class="text-[9px] font-bold text-slate-500 mb-1.5 uppercase tracking-wider">Min</span>
|
||||
<div class="h-32 overflow-y-auto w-12 text-center rounded-lg border border-border bg-popover scrollbar-none timepicker-col-minutes"></div>
|
||||
</div>
|
||||
|
||||
<!-- AM/PM Selector -->
|
||||
<div class="flex flex-col items-center ml-1">
|
||||
<span class="text-[9px] font-bold text-slate-500 mb-1.5 uppercase tracking-wider">Am/Pm</span>
|
||||
<div class="flex flex-col gap-1 w-12">
|
||||
<button type="button" class="timepicker-ampm-btn py-1.5 rounded-lg text-xs font-bold hover:bg-secondary transition text-muted-foreground">AM</button>
|
||||
<button type="button" class="timepicker-ampm-btn py-1.5 rounded-lg text-xs font-bold hover:bg-secondary transition text-muted-foreground">PM</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div></code></pre>
|
||||
<pre class="bg-popover p-4 rounded-xl border border-border overflow-x-auto text-[10px] text-sky-400 font-mono"><code>{{ "{%" }} import "components/macros.html" as ui {{ "%}" }}
|
||||
|
||||
<!-- Custom Time Picker using macro -->
|
||||
{{ "{{" }} ui::timepicker(id="my-timepicker", name="my_time", label="Time Picker", value="12:00 PM") {{ "}}" }}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends "base.html" %}
|
||||
{% import "components/macros.html" as ui %}
|
||||
|
||||
{% block title %}Form Fields & Select - Design System Wiki{% endblock %}
|
||||
|
||||
@@ -33,45 +34,22 @@
|
||||
|
||||
<!-- Demo Viewport -->
|
||||
<div id="input-sandbox" class="wiki-pane space-y-4 max-w-md py-2">
|
||||
<!-- Text field -->
|
||||
<div class="space-y-2">
|
||||
<label class="block text-xs font-semibold text-muted-foreground">Username Input</label>
|
||||
<input type="text" placeholder="e.g. dev_alice" class="block h-10 w-full rounded-xl border border-border bg-background px-4 py-2 text-sm text-white placeholder-slate-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-500/50 transition duration-200">
|
||||
</div>
|
||||
<!-- Text field using macro -->
|
||||
{{ ui::text_input(id="demo-username", name="username", label="Username Input", type="text", placeholder="e.g. dev_alice") }}
|
||||
|
||||
<!-- Password field -->
|
||||
<div class="space-y-2">
|
||||
<label class="block text-xs font-semibold text-muted-foreground">Security Password</label>
|
||||
<input type="password" placeholder="••••••••" class="block h-10 w-full rounded-xl border border-border bg-background px-4 py-2 text-sm text-white placeholder-slate-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-500/50 transition duration-200">
|
||||
</div>
|
||||
<!-- Password field using macro -->
|
||||
{{ ui::text_input(id="demo-password", name="password", label="Security Password", type="password", placeholder="••••••••") }}
|
||||
|
||||
<!-- Textarea -->
|
||||
<div class="space-y-2">
|
||||
<label class="block text-xs font-semibold text-muted-foreground">Task Details</label>
|
||||
<textarea rows="3" placeholder="Provide a detailed description of the task requirements..." class="block w-full rounded-xl border border-border bg-background px-4 py-2 text-sm text-white placeholder-slate-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-500/50 transition duration-200 resize-none"></textarea>
|
||||
</div>
|
||||
<!-- Textarea using macro -->
|
||||
{{ ui::textarea(id="demo-details", name="details", label="Task Details", placeholder="Provide a detailed description of the task requirements...") }}
|
||||
|
||||
<!-- Custom Styled Select Dropdown -->
|
||||
<div class="space-y-2">
|
||||
<label class="block text-xs font-semibold text-muted-foreground">Developer Specialization</label>
|
||||
<div class="custom-select relative inline-block w-full">
|
||||
<input type="hidden" name="specialization" class="select-value" value="Senior Rust Engineer">
|
||||
<button type="button" class="select-trigger flex h-10 w-full items-center justify-between rounded-xl border border-border bg-background px-4 py-2 text-sm text-slate-200 hover:bg-secondary/50 transition focus:outline-none focus:ring-2 focus:ring-sky-500">
|
||||
<span class="select-text">Senior Rust Engineer</span>
|
||||
<svg class="h-4 w-4 text-slate-500 transition-transform duration-200 select-chevron" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
|
||||
<polyline points="6 9 12 15 18 9"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="select-popover absolute left-0 z-20 mt-2 w-full p-1 rounded-2xl border border-border bg-popover shadow-2xl hidden animate-in fade-in slide-in-from-top-2 duration-150">
|
||||
<div class="max-h-60 overflow-y-auto p-0.5 space-y-0.5">
|
||||
<button type="button" class="select-item flex items-center w-full h-9 px-2.5 rounded-lg text-xs bg-accent text-accent-foreground font-semibold focus:bg-accent focus:text-accent-foreground focus:outline-none text-slate-205 cursor-pointer select-none text-left" data-value="Senior Rust Engineer">Senior Rust Engineer</button>
|
||||
<button type="button" class="select-item flex items-center w-full h-9 px-2.5 rounded-lg text-xs hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none text-slate-205 cursor-pointer select-none text-left" data-value="Frontend Architect">Frontend Architect</button>
|
||||
<button type="button" class="select-item flex items-center w-full h-9 px-2.5 rounded-lg text-xs hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none text-slate-205 cursor-pointer select-none text-left" data-value="DevOps Lead">DevOps Lead</button>
|
||||
<button type="button" class="select-item flex items-center w-full h-9 px-2.5 rounded-lg text-xs hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none text-slate-205 cursor-pointer select-none text-left" data-value="Database Administrator">Database Administrator</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Custom Styled Select Dropdown using paired macros -->
|
||||
{{ ui::select_open(name="specialization", label="Developer Specialization", current_value="Senior Rust Engineer", current_text="Senior Rust Engineer") }}
|
||||
{{ ui::select_item(value="Senior Rust Engineer", label="Senior Rust Engineer", is_selected=true) }}
|
||||
{{ ui::select_item(value="Frontend Architect", label="Frontend Architect") }}
|
||||
{{ ui::select_item(value="DevOps Lead", label="DevOps Lead") }}
|
||||
{{ ui::select_item(value="Database Administrator", label="Database Administrator") }}
|
||||
{{ ui::select_close() }}
|
||||
</div>
|
||||
|
||||
<!-- Code Snippet Area -->
|
||||
@@ -81,44 +59,23 @@
|
||||
<svg class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 002 2h2a2 2 0 002-2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"/></svg>
|
||||
Copy Code
|
||||
</button>
|
||||
<pre class="bg-popover p-4 rounded-xl border border-border overflow-x-auto text-[10px] text-sky-400 font-mono"><code><!-- Styled Text Input -->
|
||||
<div class="space-y-2">
|
||||
<label class="block text-xs font-semibold text-muted-foreground">Username Input</label>
|
||||
<input type="text" placeholder="e.g. dev_alice"
|
||||
class="block h-10 w-full rounded-xl border border-border bg-background px-4 py-2 text-sm text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-500/50 transition duration-200">
|
||||
</div>
|
||||
<pre class="bg-popover p-4 rounded-xl border border-border overflow-x-auto text-[10px] text-sky-400 font-mono"><code>{{ "{%" }} import "components/macros.html" as ui {{ "%}" }}
|
||||
|
||||
<!-- Styled Text Input -->
|
||||
{{ "{{" }} ui::text_input(id="demo-username", name="username", label="Username Input", type="text", placeholder="e.g. dev_alice") {{ "}}" }}
|
||||
|
||||
<!-- Styled Password Input -->
|
||||
{{ "{{" }} ui::text_input(id="demo-password", name="password", label="Security Password", type="password", placeholder="••••••••") {{ "}}" }}
|
||||
|
||||
<!-- Styled Textarea -->
|
||||
<div class="space-y-2">
|
||||
<label class="block text-xs font-semibold text-muted-foreground">Task Details</label>
|
||||
<textarea rows="3" placeholder="Provide a detailed description..."
|
||||
class="block w-full rounded-xl border border-border bg-background px-4 py-2 text-sm text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-500/50 transition duration-200 resize-none"></textarea>
|
||||
</div>
|
||||
{{ "{{" }} ui::textarea(id="demo-details", name="details", label="Task Details", placeholder="Provide a detailed description...") {{ "}}" }}
|
||||
|
||||
<!-- Custom Styled Select Dropdown (Chevron and Popover managed globally in components.js) -->
|
||||
<div class="space-y-2">
|
||||
<label class="block text-xs font-semibold text-muted-foreground">Developer Specialization</label>
|
||||
<div class="custom-select relative inline-block w-full">
|
||||
<!-- Hidden input holds the actual value for form submissions -->
|
||||
<input type="hidden" name="specialization" class="select-value" value="Senior Rust Engineer">
|
||||
|
||||
<!-- Toggle trigger button -->
|
||||
<button type="button" class="select-trigger flex h-10 w-full items-center justify-between rounded-xl border border-border bg-background px-4 py-2 text-sm text-slate-200">
|
||||
<span class="select-text">Senior Rust Engineer</span>
|
||||
<svg class="h-4 w-4 text-slate-500 transition-transform duration-200 select-chevron" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
|
||||
<polyline points="6 9 12 15 18 9"/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- Options Popover -->
|
||||
<div class="select-popover absolute left-0 z-20 mt-2 w-full p-1 rounded-2xl border border-border bg-popover shadow-2xl hidden">
|
||||
<div class="max-h-60 overflow-y-auto p-0.5 space-y-0.5">
|
||||
<button type="button" class="select-item flex items-center w-full h-9 px-2.5 rounded-lg text-xs bg-accent text-accent-foreground font-semibold text-slate-200 text-left" data-value="Senior Rust Engineer">Senior Rust Engineer</button>
|
||||
<button type="button" class="select-item flex items-center w-full h-9 px-2.5 rounded-lg text-xs hover:bg-accent hover:text-accent-foreground text-slate-200 text-left" data-value="Frontend Architect">Frontend Architect</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div></code></pre>
|
||||
<!-- Custom Styled Select Dropdown -->
|
||||
{{ "{{" }} ui::select_open(name="specialization", label="Developer Specialization", current_value="Senior Rust Engineer", current_text="Senior Rust Engineer") {{ "}}" }}
|
||||
{{ "{{" }} ui::select_item(value="Senior Rust Engineer", label="Senior Rust Engineer", is_selected=true) {{ "}}" }}
|
||||
{{ "{{" }} ui::select_item(value="Frontend Architect", label="Frontend Architect") {{ "}}" }}
|
||||
{{ "{{" }} ui::select_item(value="DevOps Lead", label="DevOps Lead") {{ "}}" }}
|
||||
{{ "{{" }} ui::select_close() {{ "}}" }}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,383 @@
|
||||
{% macro button(label, variant="primary", type="button", extra_class="", disabled=false) %}
|
||||
<button
|
||||
type="{{ type }}"
|
||||
{% if disabled %}disabled{% endif %}
|
||||
class="inline-flex items-center justify-center rounded-xl text-xs font-bold transition-all focus:outline-none focus:ring-2 focus:ring-sky-500 focus:ring-offset-2 focus:ring-offset-background px-4 py-2.5 active:scale-95
|
||||
{% if variant == "primary" %}
|
||||
bg-primary text-primary-foreground hover:opacity-90 shadow-md shadow-slate-950/20
|
||||
{% elif variant == "secondary" %}
|
||||
bg-secondary text-secondary-foreground hover:bg-secondary/80
|
||||
{% elif variant == "outline" %}
|
||||
border border-border bg-transparent hover:bg-secondary text-slate-200
|
||||
{% elif variant == "destructive" %}
|
||||
bg-destructive text-destructive-foreground hover:opacity-90 shadow-md
|
||||
{% elif variant == "indigo" %}
|
||||
bg-indigo-600 hover:bg-indigo-500 text-white shadow-md
|
||||
{% endif %}
|
||||
{% if disabled %}opacity-50 cursor-not-allowed active:scale-100{% endif %}
|
||||
{{ extra_class }}"
|
||||
>
|
||||
{{ label|safe }}
|
||||
</button>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro modal_trigger(target_id, label, variant="primary", extra_class="") %}
|
||||
<button
|
||||
data-modal-target="{{ target_id }}"
|
||||
class="inline-flex items-center justify-center rounded-xl text-xs font-bold transition-all focus:outline-none focus:ring-2 focus:ring-sky-500 focus:ring-offset-2 focus:ring-offset-background px-4 py-2.5 active:scale-95
|
||||
{% if variant == "primary" %}
|
||||
bg-primary text-primary-foreground hover:opacity-90 shadow-md shadow-slate-950/20
|
||||
{% elif variant == "secondary" %}
|
||||
bg-secondary text-secondary-foreground hover:bg-secondary/80
|
||||
{% elif variant == "outline" %}
|
||||
border border-border bg-transparent hover:bg-secondary text-slate-200
|
||||
{% elif variant == "destructive" %}
|
||||
bg-destructive text-destructive-foreground hover:opacity-90 shadow-md
|
||||
{% elif variant == "indigo" %}
|
||||
bg-indigo-600 hover:bg-indigo-500 text-white shadow-md
|
||||
{% endif %}
|
||||
{{ extra_class }}"
|
||||
>
|
||||
{{ label|safe }}
|
||||
</button>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro sheet_trigger(target_id, label, variant="primary", extra_class="") %}
|
||||
<button
|
||||
data-sheet-target="{{ target_id }}"
|
||||
class="inline-flex items-center justify-center rounded-xl text-xs font-bold transition-all focus:outline-none focus:ring-2 focus:ring-sky-500 focus:ring-offset-2 focus:ring-offset-background px-4 py-2.5 active:scale-95
|
||||
{% if variant == "primary" %}
|
||||
bg-primary text-primary-foreground hover:opacity-90 shadow-md shadow-slate-950/20
|
||||
{% elif variant == "secondary" %}
|
||||
bg-secondary text-secondary-foreground hover:bg-secondary/80
|
||||
{% elif variant == "outline" %}
|
||||
border border-border bg-transparent hover:bg-secondary text-slate-200
|
||||
{% elif variant == "destructive" %}
|
||||
bg-destructive text-destructive-foreground hover:opacity-90 shadow-md
|
||||
{% elif variant == "indigo" %}
|
||||
bg-indigo-600 hover:bg-indigo-500 text-white shadow-md
|
||||
{% endif %}
|
||||
{{ extra_class }}"
|
||||
>
|
||||
{{ label|safe }}
|
||||
</button>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro modal(id, title, content, close_label="Close") %}
|
||||
<div id="{{ id }}" class="modal-dialog fixed inset-0 z-50 flex items-center justify-center hidden" role="dialog" aria-modal="true">
|
||||
<div class="modal-backdrop fixed inset-0 bg-[#07090e]/80 backdrop-blur-sm transition-opacity duration-300"></div>
|
||||
<div class="modal-content relative z-10 w-full max-w-sm scale-95 opacity-0 transition-all duration-300 border border-border bg-popover/95 backdrop-blur-xl p-6 shadow-2xl rounded-3xl text-center">
|
||||
<div class="mx-auto flex h-10 w-10 items-center justify-center rounded-full bg-indigo-500/10 border border-indigo-500/20 text-indigo-400 mb-3">
|
||||
<svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-sm font-bold text-slate-100">{{ title }}</h3>
|
||||
<div class="text-xs text-slate-400 mt-2 leading-relaxed">{{ content|safe }}</div>
|
||||
<button class="modal-close mt-4 w-full py-2 rounded-xl bg-secondary border border-border hover:bg-secondary transition text-xs font-semibold text-slate-200">{{ close_label }}</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro modal_open(id, title) %}
|
||||
<div id="{{ id }}" class="modal-dialog fixed inset-0 z-50 flex items-center justify-center hidden" role="dialog" aria-modal="true">
|
||||
<div class="modal-backdrop fixed inset-0 bg-[#07090e]/80 backdrop-blur-sm transition-opacity duration-300"></div>
|
||||
<div class="modal-content relative z-10 w-full max-w-sm scale-95 opacity-0 transition-all duration-300 border border-border bg-popover/95 backdrop-blur-xl p-6 shadow-2xl rounded-3xl text-center">
|
||||
<div class="mx-auto flex h-10 w-10 items-center justify-center rounded-full bg-indigo-500/10 border border-indigo-500/20 text-indigo-400 mb-3">
|
||||
<svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-sm font-bold text-slate-100 mb-2">{{ title }}</h3>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro modal_close(close_label="Dismiss Modal") %}
|
||||
<button class="modal-close mt-4 w-full py-2 rounded-xl bg-secondary border border-border hover:bg-secondary transition text-xs font-semibold text-slate-200">{{ close_label }}</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro sheet_open(id, title, max_width_class="max-w-sm") %}
|
||||
<div id="{{ id }}" class="sheet-dialog fixed inset-0 z-50 overflow-hidden hidden" role="dialog" aria-modal="true">
|
||||
<div class="sheet-backdrop fixed inset-0 bg-[#07090e]/80 backdrop-blur-sm opacity-0 transition-opacity duration-300 animate-fade-in"></div>
|
||||
<div class="absolute inset-y-0 right-0 max-w-full flex pl-10">
|
||||
<div class="sheet-content w-screen {{ max_width_class }} translate-x-full transition-transform duration-300 ease-in-out border-l border-border bg-popover/95 backdrop-blur-xl p-6 shadow-2xl flex flex-col justify-between">
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center justify-between pb-3 border-b border-border">
|
||||
<h3 class="text-sm font-bold text-slate-100">{{ title }}</h3>
|
||||
<button class="sheet-close text-slate-500 hover:text-white rounded-lg p-1.5 hover:bg-secondary transition">
|
||||
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro sheet_close(save_label="Save") %}
|
||||
</div>
|
||||
{% if !save_label.is_empty() %}
|
||||
<button class="sheet-close w-full py-2.5 rounded-xl bg-indigo-650 hover:bg-indigo-600 transition text-xs font-bold text-white shadow-lg shadow-indigo-650/10">{{ save_label }}</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro search_combobox(name, label, placeholder="Search...", search_url="/developers/search", input_id="combobox-search", value_id="combobox-value") %}
|
||||
<div class="autocomplete-combobox relative">
|
||||
{% if !label.is_empty() %}
|
||||
<label class="block text-xs font-semibold text-slate-400 mb-1.5">{{ label }}</label>
|
||||
{% endif %}
|
||||
|
||||
<!-- Hidden input holding the actual selected ID to submit in the form -->
|
||||
<input type="hidden" id="{{ value_id }}" name="{{ name }}" class="combobox-value">
|
||||
|
||||
<div class="relative">
|
||||
<input type="text"
|
||||
id="{{ input_id }}"
|
||||
name="q"
|
||||
placeholder="{{ placeholder }}"
|
||||
autocomplete="off"
|
||||
hx-get="{{ search_url }}"
|
||||
hx-trigger="input changed delay:250ms, search"
|
||||
hx-target="next .combobox-results"
|
||||
hx-indicator="next .combobox-indicator"
|
||||
class="combobox-input appearance-none rounded-xl relative block w-full pl-9 pr-4 py-2.5 bg-[#0f172a]/80 border border-slate-800 placeholder-slate-500 text-white focus:outline-none focus:ring-2 focus:ring-sky-500 focus:border-sky-500 transition duration-200 text-sm">
|
||||
|
||||
<!-- Search Icon & Loading Indicator -->
|
||||
<div class="absolute left-3 top-3 text-slate-500">
|
||||
<svg class="combobox-indicator htmx-indicator animate-spin h-4 w-4 text-sky-500 hidden" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path>
|
||||
</svg>
|
||||
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Search Results Dropdown Popover -->
|
||||
<div class="combobox-results absolute z-10 w-full mt-1.5 bg-slate-900 border border-slate-800 rounded-xl shadow-2xl overflow-hidden hidden">
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro text_input(id, name, label, type="text", placeholder="", value="", required=false, extra_class="") %}
|
||||
<div class="space-y-2 {{ extra_class }}">
|
||||
{% if !label.is_empty() %}
|
||||
<label for="{{ id }}" class="block text-xs font-semibold text-muted-foreground">{{ label }}</label>
|
||||
{% endif %}
|
||||
<input
|
||||
id="{{ id }}"
|
||||
name="{{ name }}"
|
||||
type="{{ type }}"
|
||||
value="{{ value }}"
|
||||
placeholder="{{ placeholder }}"
|
||||
{% if required %}required{% endif %}
|
||||
class="block h-10 w-full rounded-xl border border-border bg-background px-4 py-2 text-sm text-white placeholder-slate-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-500/50 transition duration-200"
|
||||
>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro textarea(id, name, label, placeholder="", value="", rows="3", required=false, extra_class="") %}
|
||||
<div class="space-y-2 {{ extra_class }}">
|
||||
{% if !label.is_empty() %}
|
||||
<label for="{{ id }}" class="block text-xs font-semibold text-muted-foreground">{{ label }}</label>
|
||||
{% endif %}
|
||||
<textarea
|
||||
id="{{ id }}"
|
||||
name="{{ name }}"
|
||||
rows="{{ rows }}"
|
||||
placeholder="{{ placeholder }}"
|
||||
{% if required %}required{% endif %}
|
||||
class="block w-full rounded-xl border border-border bg-background px-4 py-2 text-sm text-white placeholder-slate-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-500/50 transition duration-200 resize-none"
|
||||
>{{ value }}</textarea>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro select_open(name, label, current_value, current_text) %}
|
||||
<div class="space-y-2">
|
||||
{% if !label.is_empty() %}
|
||||
<label class="block text-xs font-semibold text-muted-foreground">{{ label }}</label>
|
||||
{% endif %}
|
||||
<div class="custom-select relative inline-block w-full">
|
||||
<input type="hidden" name="{{ name }}" class="select-value" value="{{ current_value }}">
|
||||
<button type="button" class="select-trigger flex h-10 w-full items-center justify-between rounded-xl border border-border bg-background px-4 py-2 text-sm text-slate-200 hover:bg-secondary/50 transition focus:outline-none focus:ring-2 focus:ring-sky-500">
|
||||
<span class="select-text">{{ current_text }}</span>
|
||||
<svg class="h-4 w-4 text-slate-500 transition-transform duration-200 select-chevron" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
|
||||
<polyline points="6 9 12 15 18 9"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="select-popover absolute left-0 z-20 mt-2 w-full p-1 rounded-2xl border border-border bg-popover shadow-2xl hidden animate-in fade-in slide-in-from-top-2 duration-150">
|
||||
<div class="max-h-60 overflow-y-auto p-0.5 space-y-0.5">
|
||||
{% endmacro %}
|
||||
|
||||
{% macro select_item(value, label, is_selected=false) %}
|
||||
<button type="button" class="select-item flex items-center w-full h-9 px-2.5 rounded-lg text-xs {% if is_selected %}bg-accent text-accent-foreground font-semibold{% else %}hover:bg-accent hover:text-accent-foreground{% endif %} focus:bg-accent focus:text-accent-foreground focus:outline-none text-slate-200 cursor-pointer select-none text-left" data-value="{{ value }}">{{ label }}</button>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro select_close() %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro toggle_switch(name, label, checked=false, extra_class="") %}
|
||||
<div class="flex items-center justify-between {{ extra_class }}">
|
||||
{% if !label.is_empty() %}
|
||||
<span class="text-xs text-muted-foreground font-medium">{{ label }}</span>
|
||||
{% endif %}
|
||||
<label class="relative inline-flex items-center cursor-pointer">
|
||||
<input type="checkbox" name="{{ name }}" class="sr-only peer" {% if checked %}checked{% endif %}>
|
||||
<div class="w-9 h-5 bg-secondary rounded-full border border-border peer-checked:bg-indigo-600 peer-checked:border-indigo-500 transition-all duration-300 relative after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-slate-400 after:rounded-full after:h-[14px] after:w-[14px] after:transition-all peer-checked:after:translate-x-4 peer-checked:after:bg-white"></div>
|
||||
</label>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro checkbox(name, label, checked=false, extra_class="") %}
|
||||
<label class="flex items-center gap-3 cursor-pointer group {{ extra_class }}">
|
||||
<input type="checkbox" name="{{ name }}" class="sr-only peer" {% if checked %}checked{% endif %}>
|
||||
<div class="w-4 h-4 rounded bg-popover border border-border flex items-center justify-center peer-checked:bg-indigo-600 peer-checked:border-indigo-500 peer-checked:[&_svg]:opacity-100 transition">
|
||||
<svg class="w-2.5 h-2.5 text-white opacity-0 transition-opacity" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="4"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/></svg>
|
||||
</div>
|
||||
{% if !label.is_empty() %}
|
||||
<span class="text-xs text-muted-foreground peer-checked:text-slate-200 select-none">{{ label }}</span>
|
||||
{% endif %}
|
||||
</label>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro range_slider(name, label, min="0", max="100", value="50", extra_class="") %}
|
||||
<div class="space-y-2 {{ extra_class }}">
|
||||
{% if !label.is_empty() %}
|
||||
<label class="block text-xs font-semibold text-muted-foreground">{{ label }}</label>
|
||||
{% endif %}
|
||||
<div class="flex items-center gap-4">
|
||||
<input type="range" name="{{ name }}" min="{{ min }}" max="{{ max }}" value="{{ value }}" class="grow h-1 bg-secondary rounded-lg appearance-none cursor-pointer accent-indigo-600" oninput="this.nextElementSibling.textContent = this.value + '%'">
|
||||
<span class="text-xs font-mono font-bold text-sky-400 w-10 text-right">{{ value }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro datepicker(id, name, label, value="2026-05-30", display_text="May 30, 2026", data_year="2026", data_month="4", extra_class="") %}
|
||||
<div class="space-y-2 {{ extra_class }}">
|
||||
{% if !label.is_empty() %}
|
||||
<label class="block text-xs font-semibold text-slate-400">{{ label }}</label>
|
||||
{% endif %}
|
||||
<div class="custom-datepicker relative inline-block w-full" id="{{ id }}" data-year="{{ data_year }}" data-month="{{ data_month }}">
|
||||
<input type="hidden" name="{{ name }}" class="datepicker-value" value="{{ value }}">
|
||||
<button type="button" class="datepicker-trigger flex h-10 w-full items-center justify-between rounded-xl border border-border bg-background px-4 py-2 text-sm text-slate-200 hover:bg-secondary transition focus:outline-none focus:ring-2 focus:ring-sky-500">
|
||||
<span class="datepicker-label flex items-center gap-2">
|
||||
<svg class="h-4 w-4 text-muted-foreground" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
|
||||
</svg>
|
||||
<span class="datepicker-text">{{ display_text }}</span>
|
||||
</span>
|
||||
<svg class="h-4 w-4 text-slate-500" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><polyline points="6 9 12 15 18 9"/></svg>
|
||||
</button>
|
||||
<div class="datepicker-popover absolute left-0 z-20 mt-2 w-[270px] p-3 rounded-2xl border border-border bg-popover shadow-2xl animate-in fade-in slide-in-from-top-2 duration-200 hidden">
|
||||
<div class="flex items-center justify-between mb-3.5">
|
||||
<button type="button" class="datepicker-prev p-1.5 rounded-lg hover:bg-secondary text-muted-foreground/90 hover:text-white transition">
|
||||
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><polyline points="15 18 9 12 15 6"/></svg>
|
||||
</button>
|
||||
<span class="datepicker-month-year text-xs font-bold text-slate-200"></span>
|
||||
<button type="button" class="datepicker-next p-1.5 rounded-lg hover:bg-secondary text-muted-foreground/90 hover:text-white transition">
|
||||
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><polyline points="9 18 15 12 9 6"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="grid grid-cols-7 gap-1 text-center text-[10px] font-bold text-slate-500 mb-2">
|
||||
<span>Su</span><span>Mo</span><span>Tu</span><span>We</span><span>Th</span><span>Fr</span><span>Sa</span>
|
||||
</div>
|
||||
<div class="datepicker-days grid grid-cols-7 gap-1 text-center"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro timepicker(id, name, label, value="12:00 PM", extra_class="") %}
|
||||
<div class="space-y-2 {{ extra_class }}">
|
||||
{% if !label.is_empty() %}
|
||||
<label class="block text-xs font-semibold text-slate-405">{{ label }}</label>
|
||||
{% endif %}
|
||||
<div class="custom-timepicker relative inline-block w-full" id="{{ id }}">
|
||||
<input type="hidden" name="{{ name }}" class="timepicker-value" value="{{ value }}">
|
||||
<button type="button" class="timepicker-trigger flex h-10 w-full items-center justify-between rounded-xl border border-border bg-background px-4 py-2 text-sm text-slate-200 hover:bg-secondary transition focus:outline-none focus:ring-2 focus:ring-sky-500">
|
||||
<span class="timepicker-label flex items-center gap-2">
|
||||
<svg class="h-4 w-4 text-muted-foreground" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
<span class="timepicker-text">{{ value }}</span>
|
||||
</span>
|
||||
<svg class="h-4 w-4 text-slate-500" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><polyline points="6 9 12 15 18 9"/></svg>
|
||||
</button>
|
||||
<div class="timepicker-popover absolute right-0 sm:left-0 z-20 mt-2 w-[230px] p-3 rounded-2xl border border-border bg-popover shadow-2xl animate-in fade-in slide-in-from-top-2 duration-200 hidden">
|
||||
<div class="flex gap-2 justify-center items-center">
|
||||
<div class="flex flex-col items-center">
|
||||
<span class="text-[9px] font-bold text-slate-500 mb-1.5 uppercase tracking-wider">Hr</span>
|
||||
<div class="h-32 overflow-y-auto w-12 text-center rounded-lg border border-border bg-popover scrollbar-none timepicker-col-hours"></div>
|
||||
</div>
|
||||
<span class="text-slate-500 font-bold self-end mb-12">:</span>
|
||||
<div class="flex flex-col items-center">
|
||||
<span class="text-[9px] font-bold text-slate-500 mb-1.5 uppercase tracking-wider">Min</span>
|
||||
<div class="h-32 overflow-y-auto w-12 text-center rounded-lg border border-border bg-popover scrollbar-none timepicker-col-minutes"></div>
|
||||
</div>
|
||||
<div class="flex flex-col items-center ml-1">
|
||||
<span class="text-[9px] font-bold text-slate-500 mb-1.5 uppercase tracking-wider">Am/Pm</span>
|
||||
<div class="flex flex-col gap-1 w-12">
|
||||
<button type="button" class="timepicker-ampm-btn py-1.5 rounded-lg text-xs font-bold hover:bg-secondary transition text-muted-foreground">AM</button>
|
||||
<button type="button" class="timepicker-ampm-btn py-1.5 rounded-lg text-xs font-bold hover:bg-secondary transition text-muted-foreground">PM</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro tabs_header_open() %}
|
||||
<div class="flex border-b border-border">
|
||||
{% endmacro %}
|
||||
|
||||
{% macro tab_trigger(group, target_id, label, is_active=false) %}
|
||||
<button
|
||||
type="button"
|
||||
data-tab-group="{{ group }}"
|
||||
data-tab-target="{{ target_id }}"
|
||||
class="px-3 py-1.5 text-xs font-semibold border-b-2 focus:outline-none transition-all
|
||||
{% if is_active %}border-sky-500 text-sky-400{% else %}border-transparent text-muted-foreground hover:text-slate-200{% endif %}"
|
||||
>
|
||||
{{ label }}
|
||||
</button>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro tabs_header_close() %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro tabs_content_open() %}
|
||||
<div class="p-4 bg-card/50 rounded-xl border border-border text-xs text-muted-foreground min-h-[5rem]">
|
||||
{% endmacro %}
|
||||
|
||||
{% macro tab_pane_open(group, id, is_active=true) %}
|
||||
<div id="{{ id }}" data-tab-content-group="{{ group }}" {% if !is_active %}class="hidden"{% endif %}>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro tab_pane_close() %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro tabs_content_close() %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro accordion_open(title, is_open=false) %}
|
||||
<div class="accordion-item border border-border rounded-xl overflow-hidden bg-card/30">
|
||||
<button type="button" class="accordion-trigger w-full flex items-center justify-between px-4 py-3 text-xs font-bold text-slate-200 hover:bg-secondary/50 transition focus:outline-none">
|
||||
<span>{{ title }}</span>
|
||||
<svg class="accordion-chevron h-3 w-3 text-slate-500 transition-transform duration-200 {% if is_open %}rotate-180{% endif %}" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><polyline points="6 9 12 15 18 9"/></svg>
|
||||
</button>
|
||||
<div class="accordion-content px-4 pb-3 pt-1 text-xs text-muted-foreground {% if !is_open %}hidden{% endif %} border-t border-border leading-relaxed">
|
||||
{% endmacro %}
|
||||
|
||||
{% macro accordion_close() %}
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends "base.html" %}
|
||||
{% import "components/macros.html" as ui %}
|
||||
|
||||
{% block title %}Dialog Modals - Design System Wiki{% endblock %}
|
||||
|
||||
@@ -33,9 +34,7 @@
|
||||
|
||||
<!-- Demo Viewport -->
|
||||
<div id="modal-sandbox" class="wiki-pane py-2">
|
||||
<button data-modal-target="wiki-demo-modal" class="inline-flex items-center justify-center rounded-xl text-xs font-bold bg-indigo-600 hover:bg-indigo-500 text-white px-4 py-2.5 transition active:scale-95">
|
||||
Open Modal Dialog
|
||||
</button>
|
||||
{{ ui::modal_trigger(target_id="wiki-demo-modal", label="Open Modal Dialog", variant="indigo") }}
|
||||
</div>
|
||||
|
||||
<!-- Code Snippet Area -->
|
||||
@@ -45,27 +44,18 @@
|
||||
<svg class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 002 2h2a2 2 0 002-2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"/></svg>
|
||||
Copy Code
|
||||
</button>
|
||||
<pre class="bg-popover p-4 rounded-xl border border-border overflow-x-auto text-[10px] text-sky-400 font-mono"><code><!-- Trigger Button (points to modal element ID) -->
|
||||
<button data-modal-target="my-modal-id" class="px-4 py-2 bg-indigo-600 text-white text-xs font-bold rounded-xl">
|
||||
Open Modal
|
||||
</button>
|
||||
<pre class="bg-popover p-4 rounded-xl border border-border overflow-x-auto text-[10px] text-sky-400 font-mono"><code>{{ "{%" }} import "components/macros.html" as ui {{ "%}" }}
|
||||
|
||||
<!-- Modal Overlay Element -->
|
||||
<div id="my-modal-id" class="modal-dialog fixed inset-0 z-50 flex items-center justify-center hidden" role="dialog" aria-modal="true">
|
||||
<!-- Backdrop shadow with blur -->
|
||||
<div class="modal-backdrop fixed inset-0 bg-[#07090e]/80 backdrop-blur-sm transition-opacity duration-300"></div>
|
||||
|
||||
<!-- Modal Panel with scale / opacity transition -->
|
||||
<div class="modal-content relative z-10 w-full max-w-sm scale-95 opacity-0 transition-all duration-300 border border-border bg-popover/95 backdrop-blur-xl p-6 shadow-2xl rounded-3xl">
|
||||
<h3 class="text-sm font-bold text-slate-100">Confirmation Title</h3>
|
||||
<p class="text-xs text-muted-foreground mt-2 leading-relaxed">Are you sure you want to proceed?</p>
|
||||
|
||||
<!-- Close triggers require class 'modal-close' -->
|
||||
<button class="modal-close mt-4 w-full py-2 rounded-xl bg-secondary border border-border text-slate-200 text-xs font-semibold">
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div></code></pre>
|
||||
<!-- Trigger Button (points to modal element ID) -->
|
||||
{{ "{{" }} ui::modal_trigger(target_id="my-modal-id", label="Open Modal Dialog", variant="indigo") {{ "}}" }}
|
||||
|
||||
<!-- Option A: Simple Modal (Self-contained with text body) -->
|
||||
{{ "{{" }} ui::modal(id="my-modal-id", title="Confirmation Title", content="Are you sure you want to proceed?", close_label="Cancel") {{ "}}" }}
|
||||
|
||||
<!-- Option B: Paired Macros (For custom markup, inputs, or headers) -->
|
||||
{{ "{{" }} ui::modal_open(id="my-modal-id", title="Confirmation Title") {{ "}}" }}
|
||||
<p class="text-xs text-muted-foreground mt-2 leading-relaxed">Are you sure you want to proceed?</p>
|
||||
{{ "{{" }} ui::modal_close(close_label="Cancel") {{ "}}" }}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -136,20 +126,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Interactive Modal Sandbox Element -->
|
||||
<div id="wiki-demo-modal" class="modal-dialog fixed inset-0 z-50 flex items-center justify-center hidden" role="dialog" aria-modal="true">
|
||||
<div class="modal-backdrop fixed inset-0 bg-[#07090e]/80 backdrop-blur-sm transition-opacity duration-300"></div>
|
||||
<div class="modal-content relative z-10 w-full max-w-sm scale-95 opacity-0 transition-all duration-300 border border-border bg-popover/95 backdrop-blur-xl p-6 shadow-2xl rounded-3xl text-center">
|
||||
<div class="mx-auto flex h-10 w-10 items-center justify-center rounded-full bg-indigo-500/10 border border-indigo-500/20 text-indigo-400 mb-3">
|
||||
<svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-sm font-bold text-slate-100">Wiki Sandbox Modal</h3>
|
||||
<p class="text-xs text-slate-405 mt-2 leading-relaxed">This modal is animated and handled globally by <code>components.js</code>. Pressing escape or clicking outside closes it instantly.</p>
|
||||
<button class="modal-close mt-4 w-full py-2 rounded-xl bg-secondary border border-border hover:bg-secondary transition text-xs font-semibold text-slate-200">Dismiss Modal</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Interactive Modal Sandbox Element using paired macros -->
|
||||
{{ ui::modal_open(id="wiki-demo-modal", title="Wiki Sandbox Modal") }}
|
||||
<p class="text-xs text-slate-405 mt-2 leading-relaxed">This modal is animated and handled globally by <code>components.js</code>. Pressing escape or clicking outside closes it instantly.</p>
|
||||
{{ ui::modal_close(close_label="Dismiss Modal") }}
|
||||
|
||||
<script>
|
||||
function toggleWikiTabs(btn, paneId) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends "base.html" %}
|
||||
{% import "components/macros.html" as ui %}
|
||||
|
||||
{% block title %}Slide-over Drawers (Sheets) - Design System Wiki{% endblock %}
|
||||
|
||||
@@ -33,9 +34,7 @@
|
||||
|
||||
<!-- Demo Viewport -->
|
||||
<div id="sheet-sandbox" class="wiki-pane py-2">
|
||||
<button data-sheet-target="wiki-demo-sheet" class="inline-flex items-center justify-center rounded-xl text-xs font-bold bg-indigo-600 hover:bg-indigo-500 text-white px-4 py-2.5 transition active:scale-95">
|
||||
Open Right Drawer
|
||||
</button>
|
||||
{{ ui::sheet_trigger(target_id="wiki-demo-sheet", label="Open Right Drawer", variant="indigo") }}
|
||||
</div>
|
||||
|
||||
<!-- Code Snippet Area -->
|
||||
@@ -45,34 +44,15 @@
|
||||
<svg class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 002 2h2a2 2 0 002-2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"/></svg>
|
||||
Copy Code
|
||||
</button>
|
||||
<pre class="bg-popover p-4 rounded-xl border border-border overflow-x-auto text-[10px] text-sky-400 font-mono"><code><!-- Trigger Button (points to sheet element ID) -->
|
||||
<button data-sheet-target="my-sheet-id" class="px-4 py-2 bg-indigo-650 text-white text-xs font-bold rounded-xl">
|
||||
Open Drawer
|
||||
</button>
|
||||
<pre class="bg-popover p-4 rounded-xl border border-border overflow-x-auto text-[10px] text-sky-400 font-mono"><code>{{ "{%" }} import "components/macros.html" as ui {{ "%}" }}
|
||||
|
||||
<!-- Slide Drawer Sheet Element -->
|
||||
<div id="my-sheet-id" class="sheet-dialog fixed inset-0 z-50 overflow-hidden hidden" role="dialog" aria-modal="true">
|
||||
<!-- Backdrop -->
|
||||
<div class="sheet-backdrop fixed inset-0 bg-[#07090e]/80 backdrop-blur-sm opacity-0 transition-opacity duration-300"></div>
|
||||
|
||||
<div class="absolute inset-y-0 right-0 max-w-full flex pl-10">
|
||||
<!-- Panel with transition translate-x-full -->
|
||||
<div class="sheet-content w-screen max-w-sm translate-x-full transition-transform duration-300 bg-popover/95 border-l border-border p-6 flex flex-col justify-between">
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center justify-between pb-3 border-b border-border">
|
||||
<h3 class="text-sm font-bold text-slate-100">Settings</h3>
|
||||
<button class="sheet-close text-slate-500 hover:text-white">
|
||||
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path d="M6 18L18 6M6 6l12 12"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<p class="text-xs text-muted-foreground">Drawer Body Content</p>
|
||||
</div>
|
||||
<button class="sheet-close w-full py-2.5 bg-indigo-600 hover:bg-indigo-500 text-white text-xs font-bold rounded-xl">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div></code></pre>
|
||||
<!-- Trigger Button (points to sheet element ID) -->
|
||||
{{ "{{" }} ui::sheet_trigger(target_id="my-sheet-id", label="Open Drawer", variant="indigo") {{ "}}" }}
|
||||
|
||||
<!-- Slide Drawer Sheet Element using paired macros -->
|
||||
{{ "{{" }} ui::sheet_open(id="my-sheet-id", title="Settings", max_width_class="max-w-sm") {{ "}}" }}
|
||||
<p class="text-xs text-muted-foreground">Drawer Body Content</p>
|
||||
{{ "{{" }} ui::sheet_close(save_label="Save") {{ "}}" }}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -143,24 +123,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Interactive Sheet Sandbox Element -->
|
||||
<div id="wiki-demo-sheet" class="sheet-dialog fixed inset-0 z-50 overflow-hidden hidden" role="dialog" aria-modal="true">
|
||||
<div class="sheet-backdrop fixed inset-0 bg-[#07090e]/80 backdrop-blur-sm opacity-0 transition-opacity duration-300 animate-fade-in"></div>
|
||||
<div class="absolute inset-y-0 right-0 max-w-full flex pl-10">
|
||||
<div class="sheet-content w-screen max-w-sm translate-x-full transition-transform duration-300 ease-in-out border-l border-border bg-popover/95 backdrop-blur-xl p-6 shadow-2xl flex flex-col justify-between">
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center justify-between pb-3 border-b border-border">
|
||||
<h3 class="text-sm font-bold text-slate-100">Drawer Panel Properties</h3>
|
||||
<button class="sheet-close text-slate-500 hover:text-white rounded-lg p-1.5 hover:bg-secondary transition">
|
||||
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<p class="text-xs text-slate-405 leading-relaxed">This slide-over panel demonstrates real-time sidebar parameter adjustments, sliding in from the right edge with hardware transitions.</p>
|
||||
</div>
|
||||
<button class="sheet-close w-full py-2.5 rounded-xl bg-indigo-650 hover:bg-indigo-600 transition text-xs font-bold text-white shadow-lg shadow-indigo-650/10">Save Properties</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Interactive Sheet Sandbox Element using paired macros -->
|
||||
{{ ui::sheet_open(id="wiki-demo-sheet", title="Drawer Panel Properties", max_width_class="max-w-sm") }}
|
||||
<p class="text-xs text-slate-405 leading-relaxed">This slide-over panel demonstrates real-time sidebar parameter adjustments, sliding in from the right edge with hardware transitions.</p>
|
||||
{{ ui::sheet_close(save_label="Save Properties") }}
|
||||
|
||||
<script>
|
||||
function toggleWikiTabs(btn, paneId) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends "base.html" %}
|
||||
{% import "components/macros.html" as ui %}
|
||||
|
||||
{% block title %}Tabs & Accordions - Design System Wiki{% endblock %}
|
||||
|
||||
@@ -33,36 +34,32 @@
|
||||
|
||||
<!-- Demo Viewport -->
|
||||
<div id="tabs-sandbox" class="wiki-pane space-y-6 max-w-md py-2">
|
||||
<!-- Tabs Showcase -->
|
||||
<!-- Tabs Showcase using macros -->
|
||||
<div class="space-y-2">
|
||||
<span class="block text-xs font-semibold text-muted-foreground">Switch Tabs</span>
|
||||
<div class="flex border-b border-border">
|
||||
<button type="button" data-tab-group="wiki-tabs" data-tab-target="wiki-pane-1" class="px-4 py-2 text-xs font-semibold border-b-2 border-sky-500 text-sky-400 focus:outline-none">Overview</button>
|
||||
<button type="button" data-tab-group="wiki-tabs" data-tab-target="wiki-pane-2" class="px-4 py-2 text-xs font-semibold border-b-2 border-transparent text-muted-foreground hover:text-slate-250 focus:outline-none">Config Settings</button>
|
||||
</div>
|
||||
<div class="p-4 bg-card/50 rounded-xl border border-border text-xs text-muted-foreground min-h-[5rem]">
|
||||
<div id="wiki-pane-1" data-tab-content-group="wiki-tabs">
|
||||
{{ ui::tabs_header_open() }}
|
||||
{{ ui::tab_trigger(group="wiki-tabs", target_id="wiki-pane-1", label="Overview", is_active=true) }}
|
||||
{{ ui::tab_trigger(group="wiki-tabs", target_id="wiki-pane-2", label="Config Settings") }}
|
||||
{{ ui::tabs_header_close() }}
|
||||
|
||||
{{ ui::tabs_content_open() }}
|
||||
{{ ui::tab_pane_open(group="wiki-tabs", id="wiki-pane-1", is_active=true) }}
|
||||
Overview parameters content pane. You can place statistics, graphs, or summary tables here.
|
||||
</div>
|
||||
<div id="wiki-pane-2" data-tab-content-group="wiki-tabs" class="hidden">
|
||||
{{ ui::tab_pane_close() }}
|
||||
|
||||
{{ ui::tab_pane_open(group="wiki-tabs", id="wiki-pane-2", is_active=false) }}
|
||||
Settings updates pane. Configuration toggles, environment variables, or webhook URLs live here.
|
||||
</div>
|
||||
</div>
|
||||
{{ ui::tab_pane_close() }}
|
||||
{{ ui::tabs_content_close() }}
|
||||
</div>
|
||||
|
||||
<!-- Accordion Showcase -->
|
||||
<!-- Accordion Showcase using paired macros -->
|
||||
<div class="space-y-2">
|
||||
<span class="block text-xs font-semibold text-muted-foreground">Accordion Collapsible</span>
|
||||
<div class="space-y-2">
|
||||
<div class="accordion-item border border-border rounded-xl overflow-hidden bg-card/30">
|
||||
<button type="button" class="accordion-trigger w-full flex items-center justify-between px-4 py-3 text-xs font-bold text-slate-200 hover:bg-secondary/50 transition focus:outline-none">
|
||||
<span>Is this library dependency-free?</span>
|
||||
<svg class="accordion-chevron h-3 w-3 text-slate-500 transition-transform duration-200" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><polyline points="6 9 12 15 18 9"/></svg>
|
||||
</button>
|
||||
<div class="accordion-content px-4 pb-3 pt-1 text-xs text-muted-foreground hidden border-t border-border leading-relaxed">
|
||||
Yes! The template does not rely on Alpine.js or React. JavaScript toggles run via vanilla click listeners listening on specific class selectors.
|
||||
</div>
|
||||
</div>
|
||||
{{ ui::accordion_open(title="Is this library dependency-free?", is_open=false) }}
|
||||
Yes! The template does not rely on Alpine.js or React. JavaScript toggles run via vanilla click listeners listening on specific class selectors.
|
||||
{{ ui::accordion_close() }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -74,45 +71,28 @@
|
||||
<svg class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 002 2h2a2 2 0 002-2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"/></svg>
|
||||
Copy Code
|
||||
</button>
|
||||
<pre class="bg-popover p-4 rounded-xl border border-border overflow-x-auto text-[10px] text-sky-400 font-mono"><code><!-- 1. Tabs Layout Markup -->
|
||||
<div class="space-y-2">
|
||||
<!-- Tabs Trigger Header -->
|
||||
<div class="flex border-b border-border">
|
||||
<!-- Include class 'border-sky-500 text-sky-400' on the active trigger -->
|
||||
<button data-tab-group="my-tabs-group" data-tab-target="my-pane-1" class="px-4 py-2 border-b-2 border-sky-500 text-sky-400 text-xs font-semibold">
|
||||
Tab 1
|
||||
</button>
|
||||
<button data-tab-group="my-tabs-group" data-tab-target="my-pane-2" class="px-4 py-2 border-b-2 border-transparent text-muted-foreground hover:text-slate-200 text-xs font-semibold">
|
||||
Tab 2
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Content Panes -->
|
||||
<div class="p-4 bg-card/50 rounded-xl border border-border text-xs">
|
||||
<div id="my-pane-1" data-tab-content-group="my-tabs-group">
|
||||
Overview content...
|
||||
</div>
|
||||
<div id="my-pane-2" data-tab-content-group="my-tabs-group" class="hidden">
|
||||
Settings content...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<pre class="bg-popover p-4 rounded-xl border border-border overflow-x-auto text-[10px] text-sky-400 font-mono"><code>{{ "{%" }} import "components/macros.html" as ui {{ "%}" }}
|
||||
|
||||
<!-- 2. Accordion Markup -->
|
||||
<div class="accordion-item border border-border rounded-xl bg-card/30 overflow-hidden">
|
||||
<!-- Accordion Button Trigger -->
|
||||
<button class="accordion-trigger w-full flex items-center justify-between px-4 py-3 text-xs font-bold text-slate-200 hover:bg-secondary/50">
|
||||
<span>Section Title</span>
|
||||
<svg class="accordion-chevron h-3 w-3 text-slate-500 transition-transform duration-200" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
|
||||
<polyline points="6 9 12 15 18 9"/>
|
||||
</svg>
|
||||
</button>
|
||||
<!-- 1. Tabs Layout using paired macros -->
|
||||
{{ "{{" }} ui::tabs_header_open() {{ "}}" }}
|
||||
{{ "{{" }} ui::tab_trigger(group="my-tabs-group", target_id="my-pane-1", label="Tab 1", is_active=true) {{ "}}" }}
|
||||
{{ "{{" }} ui::tab_trigger(group="my-tabs-group", target_id="my-pane-2", label="Tab 2") {{ "}}" }}
|
||||
{{ "{{" }} ui::tabs_header_close() {{ "}}" }}
|
||||
|
||||
{{ "{{" }} ui::tabs_content_open() {{ "}}" }}
|
||||
{{ "{{" }} ui::tab_pane_open(group="my-tabs-group", id="my-pane-1", is_active=true) {{ "}}" }}
|
||||
Overview content...
|
||||
{{ "{{" }} ui::tab_pane_close() {{ "}}" }}
|
||||
|
||||
<!-- Content Panel (Hidden by default) -->
|
||||
<div class="accordion-content px-4 pb-3 pt-1 text-xs text-muted-foreground hidden border-t border-border">
|
||||
Collapsible description content.
|
||||
</div>
|
||||
</div></code></pre>
|
||||
{{ "{{" }} ui::tab_pane_open(group="my-tabs-group", id="my-pane-2", is_active=false) {{ "}}" }}
|
||||
Settings content...
|
||||
{{ "{{" }} ui::tab_pane_close() {{ "}}" }}
|
||||
{{ "{{" }} ui::tabs_content_close() {{ "}}" }}
|
||||
|
||||
<!-- 2. Accordion using paired macros -->
|
||||
{{ "{{" }} ui::accordion_open(title="Section Title", is_open=false) {{ "}}" }}
|
||||
Collapsible description content.
|
||||
{{ "{{" }} ui::accordion_close() {{ "}}" }}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends "base.html" %}
|
||||
{% import "components/macros.html" as ui %}
|
||||
|
||||
{% block title %}Switches & Checkboxes - Design System Wiki{% endblock %}
|
||||
|
||||
@@ -33,34 +34,16 @@
|
||||
|
||||
<!-- Demo Viewport -->
|
||||
<div id="toggle-sandbox" class="wiki-pane space-y-5 max-w-xs py-2">
|
||||
<!-- Toggle Switch -->
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-xs text-muted-foreground font-medium">Toggle Status</span>
|
||||
<label class="relative inline-flex items-center cursor-pointer">
|
||||
<input type="checkbox" class="sr-only peer">
|
||||
<div class="w-9 h-5 bg-secondary rounded-full border border-border peer-checked:bg-indigo-600 peer-checked:border-indigo-500 transition-all duration-300 relative after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-slate-400 after:rounded-full after:h-[14px] after:w-[14px] after:transition-all peer-checked:after:translate-x-4 peer-checked:after:bg-white"></div>
|
||||
</label>
|
||||
</div>
|
||||
<!-- Toggle Switch using macro -->
|
||||
{{ ui::toggle_switch(name="demo_toggle", label="Toggle Status", checked=false) }}
|
||||
|
||||
<!-- Custom Checkbox -->
|
||||
<!-- Custom Checkbox using macro -->
|
||||
<div class="flex flex-col gap-3">
|
||||
<label class="flex items-center gap-3 cursor-pointer group">
|
||||
<input type="checkbox" class="sr-only peer" checked>
|
||||
<div class="w-4 h-4 rounded bg-popover border border-border flex items-center justify-center peer-checked:bg-indigo-600 peer-checked:border-indigo-500 peer-checked:[&_svg]:opacity-100 transition">
|
||||
<svg class="w-2.5 h-2.5 text-white opacity-0 transition-opacity" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="4"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/></svg>
|
||||
</div>
|
||||
<span class="text-xs text-muted-foreground peer-checked:text-slate-200">Enable Email notifications</span>
|
||||
</label>
|
||||
{{ ui::checkbox(name="demo_checkbox", label="Enable Email notifications", checked=true) }}
|
||||
</div>
|
||||
|
||||
<!-- Range Slider -->
|
||||
<div class="space-y-2">
|
||||
<label class="block text-xs font-semibold text-muted-foreground">Range Slider (0-100%)</label>
|
||||
<div class="flex items-center gap-4">
|
||||
<input type="range" min="0" max="100" value="50" class="grow h-1 bg-secondary rounded-lg appearance-none cursor-pointer accent-indigo-600" oninput="this.nextElementSibling.textContent = this.value + '%'">
|
||||
<span class="text-xs font-mono font-bold text-sky-400 w-10 text-right">50%</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Range Slider using macro -->
|
||||
{{ ui::range_slider(name="demo_slider", label="Range Slider (0-100%)", min="0", max="100", value="50") }}
|
||||
</div>
|
||||
|
||||
<!-- Code Snippet Area -->
|
||||
@@ -70,34 +53,16 @@
|
||||
<svg class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 002 2h2a2 2 0 002-2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"/></svg>
|
||||
Copy Code
|
||||
</button>
|
||||
<pre class="bg-popover p-4 rounded-xl border border-border overflow-x-auto text-[10px] text-sky-400 font-mono"><code><!-- 1. Toggle Switch Variant -->
|
||||
<label class="relative inline-flex items-center cursor-pointer">
|
||||
<!-- sr-only: visually hides native input; peer: lets siblings styled with 'peer-checked:' react to its state -->
|
||||
<input type="checkbox" class="sr-only peer">
|
||||
<!-- bg-secondary: base slider bg; peer-checked:bg-indigo-600: checked slider bg;
|
||||
after: absolute round knob dot; peer-checked:after:translate-x-4: moves knob when checked -->
|
||||
<div class="w-9 h-5 bg-secondary rounded-full border border-border peer-checked:bg-indigo-600 peer-checked:border-indigo-500 transition-all duration-300 relative after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-slate-400 after:rounded-full after:h-[14px] after:w-[14px] after:transition-all peer-checked:after:translate-x-4 peer-checked:after:bg-white"></div>
|
||||
</label>
|
||||
<pre class="bg-popover p-4 rounded-xl border border-border overflow-x-auto text-[10px] text-sky-400 font-mono"><code>{{ "{%" }} import "components/macros.html" as ui {{ "%}" }}
|
||||
|
||||
<!-- 1. Toggle Switch Variant -->
|
||||
{{ "{{" }} ui::toggle_switch(name="demo_toggle", label="Toggle Status", checked=false) {{ "}}" }}
|
||||
|
||||
<!-- 2. Custom Checkbox -->
|
||||
<label class="flex items-center gap-3 cursor-pointer group">
|
||||
<!-- sr-only peer hides checkbox input globally but exposes its state -->
|
||||
<input type="checkbox" class="sr-only peer">
|
||||
<!-- peer-checked:[&_svg]:opacity-100: uses a descendant selector to show the checkmark when checked -->
|
||||
<div class="w-4 h-4 rounded bg-popover border border-border flex items-center justify-center peer-checked:bg-indigo-600 peer-checked:border-indigo-500 peer-checked:[&_svg]:opacity-100 transition">
|
||||
<svg class="w-2.5 h-2.5 text-white opacity-0 transition-opacity" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/>
|
||||
</svg>
|
||||
</div>
|
||||
<span class="text-xs text-muted-foreground peer-checked:text-slate-200 select-none">Label Option</span>
|
||||
</label>
|
||||
{{ "{{" }} ui::checkbox(name="demo_checkbox", label="Enable Email notifications", checked=true) {{ "}}" }}
|
||||
|
||||
<!-- 3. Custom Range Slider (accent-indigo-600 styles input thumb in modern browsers) -->
|
||||
<div class="flex items-center gap-4">
|
||||
<!-- oninput: updates text element sibling content to display slider value -->
|
||||
<input type="range" min="0" max="100" value="50" class="grow h-1 bg-secondary rounded-lg appearance-none cursor-pointer accent-indigo-600" oninput="this.nextElementSibling.textContent = this.value + '%'">
|
||||
<span class="text-xs font-mono font-bold text-sky-400 w-10 text-right">50%</span>
|
||||
</div></code></pre>
|
||||
<!-- 3. Custom Range Slider -->
|
||||
{{ "{{" }} ui::range_slider(name="demo_slider", label="Range Slider (0-100%)", min="0", max="100", value="50") {{ "}}" }}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user