nanoui 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 82bc23f334e4806d1a963f7076b8ae2a91ccbd6cc29661313ccb183a390271e6
4
- data.tar.gz: dc7f106ca2619cbeeb427f5adf5bd61f9dbe874a904cf3d8aba830935f1c7c23
3
+ metadata.gz: 7853589240e9826e1a84eacb068ffd3ec668c5c459081cfe3a8d93fd4231c096
4
+ data.tar.gz: d62fa64e526a04c473f30355e0cde98c5441a7e136bff1813ff08a376af0728f
5
5
  SHA512:
6
- metadata.gz: 86caab6ae5da7d790a717af769fbc5af29ab6ab9a0581a9fe5c1a4469171f48aef242af66d805dfebe03db9818b57a42a107b101312a7d1fa922176500e1ef28
7
- data.tar.gz: f3b1f8bd33b51c66be3bc939302d7a7f3acc4d42272863e49aa336257409a61805e22f35ddb4a1787c17d4eff396a0cc4da0030cc239d3e0b499f55b343633c3
6
+ metadata.gz: bb21d047b98e632f831fdf5dc96a0001f7cb6222ca566dd60b72a3b23ac5968f15b6b07dc0a4d0b7a7c35410607c4f1defcc1a858ce468a50f165f085e5c0a96
7
+ data.tar.gz: f1710c2cc03164d20663095e0b0acb7fae811498f754c43c23236d02c99e400c6e85f5771168ae56a628d7526c3181049af08886e6957df09e5bc9c9e770f1a7
data/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.0 (2026-04-17)
4
+
5
+ ### Added
6
+
7
+ - **Stat** component — dashboard KPI card with label, value, delta, and optional footer (CSS only)
8
+ - **Empty state** component — centered "no data yet" / "get started" layout with icon, title, description, and actions (CSS only)
9
+ - **Timeline** component — vertical activity feed with color-coded markers for retry history, audit logs, and status changes (CSS only)
10
+ - **Checklist** component — onboarding / setup checklist with pending / current / done states and optional progress summary (CSS only)
11
+ - **Copy** component — one-click copy-to-clipboard button with transient success state and `execCommand` fallback (CSS + Stimulus)
12
+ - **Code** component — monospace block for snippets, inline fragments, and terminal-style output (CSS only)
13
+ - **Upload** component — drag-and-drop file input with preview, size/type validation, and accessible fallback (CSS + Stimulus)
14
+ - **Data-table Stimulus controller** — click-to-sort headers (`string` / `number` / `date`), dispatches `nanoui-data-table:sort` for server-side sorting; paired with new CSS for `.nano-table__header--sortable` and `.nano-table-pagination`
15
+ - **Toast action buttons** — `.nano-toast__action` is now a fully styled inline CTA that adopts each variant's accent color
16
+ - New component groups: `dashboard` (stat, empty, timeline, checklist) and `utilities` (copy, code)
17
+ - GitHub Actions workflows for CI (RSpec on Ruby 3.1 / 3.2 / 3.3 + Rubocop) and Release (automated gem publish on `v*.*.*` tags)
18
+ - Rubocop config with `rubocop-rspec` plugin
19
+
20
+ ### Changed
21
+
22
+ - Component count: 23 → 30
23
+ - Stimulus controllers: 9 → 12
24
+ - Component generator now maps each component to its controller files via `CONTROLLER_FILES`; installing `table` automatically installs the richer `data_table` controller
25
+ - Gemspec declares `rubocop` and `rubocop-rspec` as development dependencies
26
+
3
27
  ## 0.5.0 (2026-03-21)
4
28
 
5
29
  ### Added
data/README.md CHANGED
@@ -2,15 +2,17 @@
2
2
 
3
3
  Vanilla CSS + Stimulus component library for Rails. Zero runtime dependencies.
4
4
 
5
- 23 components. Semantic HTML. Accessible by default. No build step.
5
+ 30 components. Semantic HTML. Accessible by default. No build step.
6
6
 
7
- **[Documentation & Live Previews](https://chille1987.github.io/nanoui/)** — Browse all 23 components with interactive examples.
7
+ **[Documentation & Live Previews](https://chille1987.github.io/nanoui/)** — Browse all 30 components with interactive examples.
8
8
 
9
- ## What's New in v0.5.0
9
+ ## What's New in v0.6.0
10
10
 
11
- - **5 new components** — Navbar, Sidebar, Breadcrumb, Avatar, and Skeleton
12
- - **Native CSS nesting** — All component CSS refactored to use native nesting for better readability
13
- - **New component groups** — `navigation` (navbar, sidebar, breadcrumb) and `feedback` (avatar, skeleton)
11
+ - **7 new components** — Stat, Empty state, Timeline, Checklist, Copy, Code, Upload
12
+ - **Sortable data tables** — `nanoui-data-table` Stimulus controller with client- or server-side sorting + pagination styling
13
+ - **Toast action buttons** — inline CTAs styled to match each toast variant
14
+ - **New component groups** — `dashboard` (stat, empty, timeline, checklist) and `utilities` (copy, code)
15
+ - **CI + release automation** — GitHub Actions for RSpec / Rubocop and tag-driven gem publishing
14
16
 
15
17
  ## Installation
16
18
 
@@ -61,15 +63,18 @@ If NanoUI falls back to system fonts instead of Inter:
61
63
  With Rails 8 + importmap + `eagerLoadControllersFrom`, controllers auto-register by file name. Rename the controllers with a `nanoui_` prefix:
62
64
 
63
65
  ```
64
- nanoui_dialog_controller.js → data-controller="nanoui-dialog"
65
- nanoui_dropdown_controller.js → data-controller="nanoui-dropdown"
66
- nanoui_tooltip_controller.js → data-controller="nanoui-tooltip"
67
- nanoui_toast_controller.js → data-controller="nanoui-toast"
68
- nanoui_tabs_controller.js → data-controller="nanoui-tabs"
69
- nanoui_accordion_controller.js → data-controller="nanoui-accordion"
70
- nanoui_switch_controller.js → data-controller="nanoui-switch"
71
- nanoui_navbar_controller.js → data-controller="nanoui-navbar"
72
- nanoui_sidebar_controller.js → data-controller="nanoui-sidebar"
66
+ nanoui_dialog_controller.js → data-controller="nanoui-dialog"
67
+ nanoui_dropdown_controller.js → data-controller="nanoui-dropdown"
68
+ nanoui_tooltip_controller.js → data-controller="nanoui-tooltip"
69
+ nanoui_toast_controller.js → data-controller="nanoui-toast"
70
+ nanoui_tabs_controller.js → data-controller="nanoui-tabs"
71
+ nanoui_accordion_controller.js → data-controller="nanoui-accordion"
72
+ nanoui_switch_controller.js → data-controller="nanoui-switch"
73
+ nanoui_navbar_controller.js → data-controller="nanoui-navbar"
74
+ nanoui_sidebar_controller.js → data-controller="nanoui-sidebar"
75
+ nanoui_copy_controller.js → data-controller="nanoui-copy"
76
+ nanoui_upload_controller.js → data-controller="nanoui-upload"
77
+ nanoui_data_table_controller.js → data-controller="nanoui-data-table"
73
78
  ```
74
79
 
75
80
  Or register manually:
@@ -583,6 +588,211 @@ Loading placeholder with shimmer animation.
583
588
 
584
589
  ---
585
590
 
591
+ ### Dashboard
592
+
593
+ #### Stat
594
+
595
+ Dashboard KPI card with label, value, optional delta, helper text, and footer. Group multiples with `.nano-stat-grid`.
596
+
597
+ ```html
598
+ <div class="nano-stat">
599
+ <span class="nano-stat__label">Recovered revenue</span>
600
+ <span class="nano-stat__value">$4,238</span>
601
+ <span class="nano-stat__delta nano-stat__delta--up">12% vs last month</span>
602
+ </div>
603
+
604
+ <div class="nano-stat-grid">
605
+ <div class="nano-stat">…</div>
606
+ <div class="nano-stat nano-stat--compact">…</div>
607
+ </div>
608
+ ```
609
+
610
+ **Variants:** default, `--elevated`, `--bordered`, `--compact`
611
+
612
+ #### Empty state
613
+
614
+ Centered layout for "no data yet" or "get started" screens.
615
+
616
+ ```html
617
+ <div class="nano-empty">
618
+ <div class="nano-empty__icon"><!-- SVG --></div>
619
+ <h3 class="nano-empty__title">No failed payments yet</h3>
620
+ <p class="nano-empty__description">When Stripe reports a failed charge we'll queue retries here.</p>
621
+ <div class="nano-empty__actions">
622
+ <button class="nano-btn nano-btn--primary">Connect Stripe</button>
623
+ </div>
624
+ </div>
625
+ ```
626
+
627
+ **Variants:** default, `--bordered` (dashed outline), `--compact`
628
+
629
+ #### Timeline
630
+
631
+ Vertical activity feed with color-coded markers.
632
+
633
+ ```html
634
+ <ol class="nano-timeline">
635
+ <li class="nano-timeline__item nano-timeline__item--destructive">
636
+ <span class="nano-timeline__marker"><!-- SVG --></span>
637
+ <span class="nano-timeline__line"></span>
638
+ <div class="nano-timeline__body">
639
+ <div class="nano-timeline__header">
640
+ <span class="nano-timeline__title">Payment failed</span>
641
+ <time class="nano-timeline__time">3 days ago</time>
642
+ </div>
643
+ <span class="nano-timeline__description">Card declined — insufficient funds.</span>
644
+ </div>
645
+ </li>
646
+ </ol>
647
+ ```
648
+
649
+ **Item states:** default, `--info`, `--success`, `--warning`, `--destructive`
650
+
651
+ #### Checklist
652
+
653
+ Onboarding / setup checklist with pending, current, and done states. Drive the summary bar with the `--nano-checklist-progress` CSS variable.
654
+
655
+ ```html
656
+ <div class="nano-checklist">
657
+ <div class="nano-checklist__summary">
658
+ <span><span class="nano-checklist__count">2 of 4</span> complete</span>
659
+ <span class="nano-checklist__bar" style="--nano-checklist-progress: 50%;">
660
+ <span class="nano-checklist__bar-fill"></span>
661
+ </span>
662
+ </div>
663
+ <div class="nano-checklist__item nano-checklist__item--done">
664
+ <span class="nano-checklist__indicator"><!-- check SVG --></span>
665
+ <div class="nano-checklist__body">
666
+ <span class="nano-checklist__title">Connect Stripe</span>
667
+ </div>
668
+ </div>
669
+ <div class="nano-checklist__item nano-checklist__item--current">
670
+ <span class="nano-checklist__indicator">3</span>
671
+ <div class="nano-checklist__body">
672
+ <span class="nano-checklist__title">Pick an email template</span>
673
+ <a class="nano-checklist__action" href="#">Choose template →</a>
674
+ </div>
675
+ </div>
676
+ </div>
677
+ ```
678
+
679
+ ---
680
+
681
+ ### Utilities
682
+
683
+ #### Copy to clipboard
684
+
685
+ Button (or button + value) that copies to clipboard with a transient success state. Uses the Clipboard API with a `document.execCommand` fallback.
686
+
687
+ ```html
688
+ <div class="nano-copy" data-controller="nanoui-copy">
689
+ <span class="nano-copy__value" data-nanoui-copy-target="source">
690
+ https://app.example.com/webhooks/stripe
691
+ </span>
692
+ <button class="nano-copy__button"
693
+ data-nanoui-copy-target="button"
694
+ data-action="nanoui-copy#copy"
695
+ type="button">
696
+ <span data-nanoui-copy-target="idle">Copy</span>
697
+ <span data-nanoui-copy-target="copied" hidden>Copied</span>
698
+ </button>
699
+ </div>
700
+ ```
701
+
702
+ Dispatches `nanoui-copy:copied` with `{ text }` in the event detail.
703
+
704
+ #### Code
705
+
706
+ Monospace block with optional header, inline variant, terminal (dark) variant, and wrap mode.
707
+
708
+ ```html
709
+ <div class="nano-code">
710
+ <div class="nano-code__header">
711
+ <span class="nano-code__language">shell</span>
712
+ </div>
713
+ <pre class="nano-code__body"><code>stripe listen --forward-to localhost:3000/webhooks/stripe</code></pre>
714
+ </div>
715
+
716
+ <!-- Inline -->
717
+ <p>Set <code class="nano-code nano-code--inline">STRIPE_WEBHOOK_SECRET</code> before deploying.</p>
718
+ ```
719
+
720
+ **Variants:** default, `--inline`, `--terminal`, `--wrap`
721
+
722
+ ---
723
+
724
+ ### Data table enhancements (built on Table)
725
+
726
+ Add sortable columns and pagination to any existing table. Installing the `table` component also copies `nanoui_data_table_controller.js`.
727
+
728
+ ```html
729
+ <div class="nano-table-wrapper" data-controller="nanoui-data-table">
730
+ <table class="nano-table nano-table--hoverable">
731
+ <thead class="nano-table__head">
732
+ <tr>
733
+ <th class="nano-table__header nano-table__header--sortable"
734
+ data-nanoui-data-table-target="header"
735
+ data-sort-key="amount"
736
+ data-sort-type="number"
737
+ data-action="click->nanoui-data-table#sort">
738
+ <button type="button" class="nano-table__sort">Amount</button>
739
+ </th>
740
+ </tr>
741
+ </thead>
742
+ <tbody class="nano-table__body" data-nanoui-data-table-target="body">
743
+ <tr class="nano-table__row" data-nanoui-data-table-target="row">
744
+ <td class="nano-table__cell" data-sort-value="49">$49.00</td>
745
+ </tr>
746
+ </tbody>
747
+ </table>
748
+ </div>
749
+
750
+ <nav class="nano-table-pagination" aria-label="Table pagination">
751
+ <span class="nano-table-pagination__info">Showing <strong>1–10</strong> of <strong>42</strong></span>
752
+ <span class="nano-table-pagination__controls">
753
+ <a class="nano-table-pagination__button" aria-current="page" href="?page=1">1</a>
754
+ <a class="nano-table-pagination__button" href="?page=2">2</a>
755
+ </span>
756
+ </nav>
757
+ ```
758
+
759
+ Set `data-nanoui-data-table-server-value="true"` to skip client-side sorting and only dispatch the `nanoui-data-table:sort` event (e.g. for Turbo Frames).
760
+
761
+ Supported `data-sort-type` values: `string` (default), `number`, `date`.
762
+
763
+ ---
764
+
765
+ ### Forms (extended)
766
+
767
+ #### Upload
768
+
769
+ Drag-and-drop file input with preview, `maxSize` / `accept` validation, and accessible fallback to the native picker. Wraps a hidden `<input type="file">` so standard form submission works unchanged (including `form_with` + Active Storage).
770
+
771
+ ```html
772
+ <div class="nano-upload"
773
+ data-controller="nanoui-upload"
774
+ data-nanoui-upload-accept-value="image/*"
775
+ data-nanoui-upload-max-size-value="2097152"
776
+ data-action="dragover->nanoui-upload#onDragover dragleave->nanoui-upload#onDragleave drop->nanoui-upload#onDrop">
777
+ <input type="file" name="logo" class="nano-upload__input"
778
+ data-nanoui-upload-target="input"
779
+ data-action="change->nanoui-upload#onChange">
780
+ <label class="nano-upload__dropzone"
781
+ data-nanoui-upload-target="dropzone"
782
+ data-action="click->nanoui-upload#openPicker"
783
+ tabindex="0">
784
+ <span class="nano-upload__icon"><!-- SVG --></span>
785
+ <span class="nano-upload__prompt"><strong>Click to upload</strong> or drag and drop</span>
786
+ <span class="nano-upload__hint">PNG or JPG up to 2 MB</span>
787
+ </label>
788
+ <!-- preview element is toggled automatically via data-state -->
789
+ </div>
790
+ ```
791
+
792
+ Dispatches `nanoui-upload:selected` and `nanoui-upload:removed`.
793
+
794
+ ---
795
+
586
796
  ## Design Tokens
587
797
 
588
798
  Customize your theme by editing the CSS custom properties:
@@ -614,11 +824,13 @@ All components update automatically, including dark mode.
614
824
  | Group | Components |
615
825
  |---|---|
616
826
  | **Essentials** | Button, Input, Label, Card, Badge, Alert |
617
- | **Forms** | Checkbox, Radio, Switch, Select |
827
+ | **Forms** | Checkbox, Radio, Switch, Select, Upload |
618
828
  | **Overlays** | Dialog, Dropdown, Tooltip, Toast |
619
- | **Data** | Table, Tabs, Accordion, Progress |
829
+ | **Data** | Table (with sortable data-table controller), Tabs, Accordion, Progress |
620
830
  | **Navigation** | Navbar, Sidebar, Breadcrumb |
621
831
  | **Feedback** | Avatar, Skeleton |
832
+ | **Dashboard** | Stat, Empty state, Timeline, Checklist |
833
+ | **Utilities** | Copy to clipboard, Code block |
622
834
  | **Layout** | Container |
623
835
 
624
836
  ```bash
@@ -628,6 +840,8 @@ rails generate nanoui:component --group overlays
628
840
  rails generate nanoui:component --group data
629
841
  rails generate nanoui:component --group navigation
630
842
  rails generate nanoui:component --group feedback
843
+ rails generate nanoui:component --group dashboard
844
+ rails generate nanoui:component --group utilities
631
845
  rails generate nanoui:component --all
632
846
  ```
633
847
 
@@ -27,6 +27,13 @@ module Nanoui
27
27
  breadcrumb
28
28
  avatar
29
29
  skeleton
30
+ stat
31
+ empty
32
+ timeline
33
+ checklist
34
+ copy
35
+ code
36
+ upload
30
37
  container
31
38
  ].freeze
32
39
 
@@ -38,25 +45,45 @@ module Nanoui
38
45
  class_option :all, type: :boolean, default: false, desc: "Install all components"
39
46
 
40
47
  GROUPS = {
41
- "essentials" => %w[button input label card badge alert],
42
- "forms" => %w[button input label checkbox radio switch select badge alert],
43
- "overlays" => %w[dialog dropdown tooltip toast],
44
- "data" => %w[table tabs accordion progress],
45
- "navigation" => %w[navbar sidebar breadcrumb],
46
- "feedback" => %w[avatar skeleton],
47
- "layout" => %w[container],
48
+ "essentials" => %w[button input label card badge alert],
49
+ "forms" => %w[button input label checkbox radio switch select badge alert upload],
50
+ "overlays" => %w[dialog dropdown tooltip toast],
51
+ "data" => %w[table tabs accordion progress],
52
+ "navigation" => %w[navbar sidebar breadcrumb],
53
+ "feedback" => %w[avatar skeleton],
54
+ "dashboard" => %w[stat empty timeline checklist],
55
+ "utilities" => %w[copy code],
56
+ "layout" => %w[container],
48
57
  }.freeze
49
58
 
50
- STIMULUS_COMPONENTS = %w[dialog dropdown tooltip toast tabs accordion switch navbar sidebar].freeze
59
+ # Maps a component name to the Stimulus controller files it installs.
60
+ # Most components ship with a same-named controller, but `table` ships
61
+ # with the richer `data_table` controller for sort + pagination behavior.
62
+ CONTROLLER_FILES = {
63
+ "dialog" => %w[dialog],
64
+ "dropdown" => %w[dropdown],
65
+ "tooltip" => %w[tooltip],
66
+ "toast" => %w[toast],
67
+ "tabs" => %w[tabs],
68
+ "accordion" => %w[accordion],
69
+ "switch" => %w[switch],
70
+ "navbar" => %w[navbar],
71
+ "sidebar" => %w[sidebar],
72
+ "copy" => %w[copy],
73
+ "upload" => %w[upload],
74
+ "table" => %w[data_table],
75
+ }.freeze
76
+
77
+ STIMULUS_COMPONENTS = CONTROLLER_FILES.keys.freeze
51
78
 
52
79
  def resolve_components
53
80
  @resolved = if options[:all]
54
- GROUPS.values.flatten.uniq
55
- elsif options[:group]
56
- GROUPS.fetch(options[:group]) { abort "Unknown group: #{options[:group]}" }
57
- else
58
- components
59
- end
81
+ GROUPS.values.flatten.uniq
82
+ elsif options[:group]
83
+ GROUPS.fetch(options[:group]) { abort "Unknown group: #{options[:group]}" }
84
+ else
85
+ components
86
+ end
60
87
 
61
88
  abort "Specify components, --group, or --all" if @resolved.empty?
62
89
  end
@@ -71,9 +98,10 @@ module Nanoui
71
98
 
72
99
  def copy_stimulus_controllers
73
100
  @resolved.each do |name|
74
- next unless STIMULUS_COMPONENTS.include?(name)
75
- copy_file "js/controllers/#{name}_controller.js",
76
- "app/javascript/controllers/nanoui_#{name}_controller.js"
101
+ CONTROLLER_FILES.fetch(name, []).each do |controller|
102
+ copy_file "js/controllers/#{controller}_controller.js",
103
+ "app/javascript/controllers/nanoui_#{controller}_controller.js"
104
+ end
77
105
  end
78
106
  end
79
107
 
@@ -0,0 +1,133 @@
1
+ .nano-checklist {
2
+ display: flex;
3
+ flex-direction: column;
4
+ gap: var(--space-2);
5
+
6
+ .nano-checklist__summary {
7
+ display: flex;
8
+ align-items: center;
9
+ justify-content: space-between;
10
+ gap: var(--space-3);
11
+ padding: var(--space-3) var(--space-4);
12
+ background-color: hsl(var(--color-muted));
13
+ border-radius: var(--radius-md);
14
+ font-size: var(--text-sm);
15
+ color: hsl(var(--color-muted-foreground));
16
+ }
17
+
18
+ .nano-checklist__count {
19
+ font-weight: var(--font-semibold);
20
+ color: hsl(var(--color-foreground));
21
+ }
22
+
23
+ .nano-checklist__bar {
24
+ flex: 1;
25
+ height: 0.375rem;
26
+ background-color: hsl(var(--color-background));
27
+ border-radius: var(--radius-full);
28
+ overflow: hidden;
29
+ }
30
+
31
+ .nano-checklist__bar-fill {
32
+ display: block;
33
+ height: 100%;
34
+ width: var(--nano-checklist-progress, 0%);
35
+ background-color: hsl(var(--color-primary));
36
+ border-radius: var(--radius-full);
37
+ transition: width var(--duration-slow) var(--ease-default);
38
+ }
39
+
40
+ .nano-checklist__item {
41
+ display: flex;
42
+ gap: var(--space-3);
43
+ padding: var(--space-4);
44
+ border: 1px solid hsl(var(--color-border));
45
+ border-radius: var(--radius-md);
46
+ background-color: hsl(var(--color-card));
47
+ transition: border-color var(--duration-fast) var(--ease-default),
48
+ background-color var(--duration-fast) var(--ease-default);
49
+
50
+ &--current {
51
+ border-color: hsl(var(--color-primary) / 0.5);
52
+ background-color: hsl(var(--color-primary) / 0.04);
53
+ }
54
+
55
+ &--done {
56
+ border-color: hsl(var(--color-success) / 0.4);
57
+ background-color: hsl(var(--color-success) / 0.04);
58
+
59
+ .nano-checklist__title {
60
+ color: hsl(var(--color-muted-foreground));
61
+ }
62
+ }
63
+ }
64
+
65
+ .nano-checklist__indicator {
66
+ display: inline-flex;
67
+ align-items: center;
68
+ justify-content: center;
69
+ flex-shrink: 0;
70
+ width: 1.75rem;
71
+ height: 1.75rem;
72
+ margin-top: var(--space-0-5);
73
+ border-radius: var(--radius-full);
74
+ border: 2px solid hsl(var(--color-border));
75
+ background-color: hsl(var(--color-background));
76
+ font-size: var(--text-xs);
77
+ font-weight: var(--font-semibold);
78
+ color: hsl(var(--color-muted-foreground));
79
+
80
+ svg {
81
+ width: 0.875rem;
82
+ height: 0.875rem;
83
+ }
84
+ }
85
+
86
+ .nano-checklist__item--current .nano-checklist__indicator {
87
+ border-color: hsl(var(--color-primary));
88
+ color: hsl(var(--color-primary));
89
+ }
90
+
91
+ .nano-checklist__item--done .nano-checklist__indicator {
92
+ border-color: hsl(var(--color-success));
93
+ background-color: hsl(var(--color-success));
94
+ color: hsl(var(--color-success-foreground));
95
+ }
96
+
97
+ .nano-checklist__body {
98
+ display: flex;
99
+ flex-direction: column;
100
+ gap: var(--space-1);
101
+ flex: 1;
102
+ min-width: 0;
103
+ }
104
+
105
+ .nano-checklist__title {
106
+ font-size: var(--text-sm);
107
+ font-weight: var(--font-semibold);
108
+ color: hsl(var(--color-foreground));
109
+ line-height: var(--leading-tight);
110
+ }
111
+
112
+ .nano-checklist__description {
113
+ font-size: var(--text-sm);
114
+ color: hsl(var(--color-muted-foreground));
115
+ line-height: var(--leading-normal);
116
+ }
117
+
118
+ .nano-checklist__action {
119
+ display: inline-flex;
120
+ align-items: center;
121
+ gap: var(--space-1);
122
+ margin-top: var(--space-1);
123
+ font-size: var(--text-sm);
124
+ font-weight: var(--font-medium);
125
+ color: hsl(var(--color-primary));
126
+ text-decoration: none;
127
+ align-self: flex-start;
128
+
129
+ &:hover {
130
+ text-decoration: underline;
131
+ }
132
+ }
133
+ }
@@ -0,0 +1,80 @@
1
+ .nano-code {
2
+ position: relative;
3
+ background-color: hsl(var(--color-muted));
4
+ border: 1px solid hsl(var(--color-border));
5
+ border-radius: var(--radius-md);
6
+ overflow: hidden;
7
+
8
+ .nano-code__header {
9
+ display: flex;
10
+ align-items: center;
11
+ justify-content: space-between;
12
+ gap: var(--space-2);
13
+ padding: var(--space-2) var(--space-3);
14
+ background-color: hsl(var(--color-background));
15
+ border-bottom: 1px solid hsl(var(--color-border));
16
+ font-size: var(--text-xs);
17
+ color: hsl(var(--color-muted-foreground));
18
+ }
19
+
20
+ .nano-code__language {
21
+ font-family: var(--font-mono);
22
+ font-weight: var(--font-semibold);
23
+ text-transform: uppercase;
24
+ letter-spacing: var(--tracking-wide);
25
+ }
26
+
27
+ .nano-code__actions {
28
+ display: inline-flex;
29
+ align-items: center;
30
+ gap: var(--space-1);
31
+ }
32
+
33
+ .nano-code__body {
34
+ display: block;
35
+ margin: 0;
36
+ padding: var(--space-4);
37
+ font-family: var(--font-mono);
38
+ font-size: var(--text-sm);
39
+ line-height: var(--leading-relaxed);
40
+ color: hsl(var(--color-foreground));
41
+ overflow-x: auto;
42
+ white-space: pre;
43
+ tab-size: 2;
44
+ }
45
+
46
+ &--inline {
47
+ display: inline-block;
48
+ padding: var(--space-0-5) var(--space-1-5);
49
+ margin: 0;
50
+ background-color: hsl(var(--color-muted));
51
+ border: 1px solid hsl(var(--color-border));
52
+ border-radius: var(--radius-sm);
53
+ font-family: var(--font-mono);
54
+ font-size: 0.9em;
55
+ color: hsl(var(--color-foreground));
56
+ overflow: visible;
57
+ }
58
+
59
+ &--terminal {
60
+ background-color: hsl(var(--color-foreground));
61
+ border-color: hsl(var(--color-foreground));
62
+
63
+ .nano-code__header {
64
+ background-color: hsl(var(--color-foreground) / 0.9);
65
+ border-bottom-color: hsl(var(--color-muted-foreground) / 0.3);
66
+ color: hsl(var(--color-background) / 0.7);
67
+ }
68
+
69
+ .nano-code__body {
70
+ color: hsl(var(--color-background));
71
+ }
72
+ }
73
+
74
+ &--wrap {
75
+ .nano-code__body {
76
+ white-space: pre-wrap;
77
+ word-break: break-word;
78
+ }
79
+ }
80
+ }