stimulus_plumbers_tailwind 0.4.8 → 0.4.10

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.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +30 -0
  3. data/README.md +7 -12
  4. data/app/assets/stylesheets/stimulus_plumbers/tailwind/animations.css +18 -0
  5. data/docs/guide.md +16 -5
  6. data/lib/generators/stimulus_plumbers/tailwind/install/install_generator.rb +44 -28
  7. data/lib/stimulus_plumbers/tailwind/generators/animations_directive.rb +32 -0
  8. data/lib/stimulus_plumbers/tailwind/generators/sources_directive.rb +29 -0
  9. data/lib/stimulus_plumbers/tailwind/version.rb +1 -1
  10. data/lib/stimulus_plumbers/themes/tailwind/button.rb +1 -1
  11. data/lib/stimulus_plumbers/themes/tailwind/card.rb +11 -11
  12. data/lib/stimulus_plumbers/themes/tailwind/checklist.rb +65 -0
  13. data/lib/stimulus_plumbers/themes/tailwind/combobox.rb +3 -3
  14. data/lib/stimulus_plumbers/themes/tailwind/control.rb +2 -0
  15. data/lib/stimulus_plumbers/themes/tailwind/form/code.rb +37 -0
  16. data/lib/stimulus_plumbers/themes/tailwind/form/credit_card.rb +39 -0
  17. data/lib/stimulus_plumbers/themes/tailwind/form/field.rb +2 -2
  18. data/lib/stimulus_plumbers/themes/tailwind/form/input.rb +28 -25
  19. data/lib/stimulus_plumbers/themes/tailwind/form.rb +2 -2
  20. data/lib/stimulus_plumbers/themes/tailwind/icon.rb +10 -3
  21. data/lib/stimulus_plumbers/themes/tailwind/icons/customs/progress-ring.svg +12 -0
  22. data/lib/stimulus_plumbers/themes/tailwind/indicator.rb +49 -0
  23. data/lib/stimulus_plumbers/themes/tailwind/link.rb +1 -1
  24. data/lib/stimulus_plumbers/themes/tailwind/list.rb +13 -13
  25. data/lib/stimulus_plumbers/themes/tailwind/ordered_list.rb +66 -0
  26. data/lib/stimulus_plumbers/themes/tailwind/progress.rb +45 -0
  27. data/lib/stimulus_plumbers/themes/tailwind/timeline/group.rb +14 -5
  28. data/lib/stimulus_plumbers/themes/tailwind/timeline.rb +7 -9
  29. data/lib/stimulus_plumbers/themes/tailwind_theme.rb +12 -0
  30. metadata +11 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f570868b6706fa7a6b460b6bf78361957e3ed8a7ed3592bca0a2587966efbafe
4
- data.tar.gz: 3ec048b9fef9972184db57cb95a121408ef3269134c4351c7f97a808f523d24e
3
+ metadata.gz: 79b18f9be6a2f72a2b552a8f69cca0f96bc1d0d4092ed858c33f76e09c3b1e26
4
+ data.tar.gz: ec35944909722c5a7bbf3840573db3a51549e8afa1720f5f069951d94b146771
5
5
  SHA512:
6
- metadata.gz: 822d92416d88670f4c77c802cc87bd9c16b89cd17007f14a76d122cdcd9cbe7eb8224bf10ac6fbdb8d3f5c09abd9bec627633b78e43b880b5fcbd0ddcea16422
7
- data.tar.gz: 1dda3a7595075605a20dd5739d42e39a74b1a0c3535e8c7af33ba7f85ddb2b603914b13da493ad7bd4605502e4afd0ae22eff30824eb736bbbe06c075bae314b
6
+ metadata.gz: a35c48c1e604efc1f04581c6975f66199664c92f07e51050c492bcd4683dacd89fe16ff516ae84dca4bde164d34441923c5e42b4597f3f27fd64a4b056b07161
7
+ data.tar.gz: 0b65ffab71d672cdd08f6ed69794fe932840838a55c4f76acdf197ae3a6822a2938bbc7b58d558033bf562da6d4f76669e5a81ffddff3bc125945187929bcbca
data/CHANGELOG.md CHANGED
@@ -2,6 +2,36 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [conventional commits](https://www.conventionalcommits.org/) for commit guidelines.
4
4
 
5
+ ---
6
+ ## [0.4.9](https://github.com/ryancyq/stimulus-plumbers/compare/stimulus-plumbers-tailwind/v0.4.8..stimulus-plumbers-tailwind/v0.4.9) - 2026-07-18
7
+
8
+ ### Bug Fixes
9
+
10
+ - css source + token in generator ([#163](https://github.com/ryancyq/stimulus-plumbers/issues/163)) - ([69165f5](https://github.com/ryancyq/stimulus-plumbers/commit/69165f523242e27cae5075f635c7e66c40a862ab)) - Ryan Chang
11
+
12
+ ### Documentation
13
+
14
+ - update missing documentation ([#164](https://github.com/ryancyq/stimulus-plumbers/issues/164)) - ([edcd79d](https://github.com/ryancyq/stimulus-plumbers/commit/edcd79df3dd1739521641274863a50a32b5fcb36)) - Ryan Chang
15
+ - align code example/instructions in doc and remove stale reference ([#167](https://github.com/ryancyq/stimulus-plumbers/issues/167)) - ([b1ace4d](https://github.com/ryancyq/stimulus-plumbers/commit/b1ace4da5117b70f4f22403f727655c1586606e1)) - Ryan Chang
16
+ - update component documentation ([#173](https://github.com/ryancyq/stimulus-plumbers/issues/173)) - ([27240bc](https://github.com/ryancyq/stimulus-plumbers/commit/27240bc85265b249adba5186fd34b9c26423daa3)) - Ryan Chang
17
+
18
+ ### Features
19
+
20
+ - ordered list ([#166](https://github.com/ryancyq/stimulus-plumbers/issues/166)) - ([cda05b5](https://github.com/ryancyq/stimulus-plumbers/commit/cda05b589e2f99ba992785fdb672b0ecef026046)) - Ryan Chang
21
+ - progress ring/bar/meter ([#170](https://github.com/ryancyq/stimulus-plumbers/issues/170)) - ([b43087c](https://github.com/ryancyq/stimulus-plumbers/commit/b43087cec2b40fd8b3dced65215afdbbdfc2c435)) - Ryan Chang
22
+ - status indicator ([#171](https://github.com/ryancyq/stimulus-plumbers/issues/171)) - ([ad3f330](https://github.com/ryancyq/stimulus-plumbers/commit/ad3f330bc8d999b4977f8aeaafc8c4763229a448)) - Ryan Chang
23
+ - horizontal grouped timeline ([#172](https://github.com/ryancyq/stimulus-plumbers/issues/172)) - ([fe9b9ab](https://github.com/ryancyq/stimulus-plumbers/commit/fe9b9abfb8c3c211fe9a7282d2940326678328dc)) - Ryan Chang
24
+ - checklist ([#174](https://github.com/ryancyq/stimulus-plumbers/issues/174)) - ([35dca2a](https://github.com/ryancyq/stimulus-plumbers/commit/35dca2af29955f6f2c85fffc4efc39711ed5bb99)) - Ryan Chang
25
+
26
+ ---
27
+ ## [0.4.8](https://github.com/ryancyq/stimulus-plumbers/compare/stimulus-plumbers-tailwind/v0.4.5..stimulus-plumbers-tailwind/v0.4.8) - 2026-07-03
28
+
29
+ ### Bug Fixes
30
+
31
+ - tailwind install generator ([#154](https://github.com/ryancyq/stimulus-plumbers/issues/154)) - ([db4bda0](https://github.com/ryancyq/stimulus-plumbers/commit/db4bda0b899a463e0608229927c65326233dd3a4)) - Ryan Chang
32
+ - tailwind theme registration + activation (rails only) ([#157](https://github.com/ryancyq/stimulus-plumbers/issues/157)) - ([5998854](https://github.com/ryancyq/stimulus-plumbers/commit/5998854ed2a6ca9c67aa225f3c397e4f7bca7a51)) - Ryan Chang
33
+ - mcp doc update + new tools validations ([#162](https://github.com/ryancyq/stimulus-plumbers/issues/162)) - ([beed1bb](https://github.com/ryancyq/stimulus-plumbers/commit/beed1bb14cbfcb8ec6f757145dc6a461d357c060)) - Ryan Chang
34
+
5
35
  ---
6
36
  ## [0.4.4](https://github.com/ryancyq/stimulus-plumbers/compare/stimulus-plumbers-tailwind/v0.4.3..stimulus-plumbers-tailwind/v0.4.4) - 2026-06-30
7
37
 
data/README.md CHANGED
@@ -11,7 +11,7 @@ See [docs/guide.md](docs/guide.md) for a quick setup/theming guide.
11
11
  ## Requirements
12
12
 
13
13
  - Ruby >= 3.0
14
- - `stimulus_plumbers` >= 0.2.9
14
+ - `stimulus_plumbers` >= 0.3.1
15
15
  - Tailwind CSS v4 in your build toolchain
16
16
 
17
17
  ## Installation
@@ -33,21 +33,13 @@ StimulusPlumbers.configure do |config|
33
33
  end
34
34
  ```
35
35
 
36
- Run the install generator once to inject the required `@source` directive (and the `stimulus_plumbers` tokens.css import) into your Tailwind CSS entry file:
36
+ Run the install generator once to copy `stimulus_plumbers/tokens.css` and `stimulus_plumbers/tailwind/animations.css` into your app, then inject their relative imports and the required `@source` directive into your Tailwind CSS entry file — see [docs/guide.md](docs/guide.md) for file-detection/override details:
37
37
 
38
38
  ```bash
39
39
  bin/rails generate stimulus_plumbers:tailwind:install
40
40
  ```
41
41
 
42
- The generator checks these files in order:
43
-
44
- - `app/assets/stylesheets/application.tailwind.css`
45
- - `app/assets/stylesheets/application.css`
46
- - `app/javascript/entrypoints/application.css`
47
-
48
- Override the detected file for both the generator and rake task with `TAILWIND_CSS_FILE=/path/to/entry.css`.
49
-
50
- After the initial install, the `@source` path is kept current automatically — no manual re-run needed after `bundle update`. The engine hooks `stimulus_plumbers:tailwind:install` as a prerequisite of:
42
+ After that, the `@source` path is kept current automatically — no manual re-run needed after `bundle update`. The copied CSS remains application-owned and is never overwritten; update it intentionally when adopting new defaults. The engine hooks `stimulus_plumbers:tailwind:install` as a prerequisite of:
51
43
 
52
44
  - `assets:precompile` (Sprockets and Propshaft both define this task)
53
45
  - `tailwindcss:build` (when `tailwindcss-rails` is present)
@@ -71,9 +63,12 @@ bin/rails stimulus_plumbers:tailwind:install
71
63
  | `Tailwind::Combobox` | Combobox (date, time, dropdown, typeahead) |
72
64
  | `Tailwind::Form` | Form fields, labels, errors |
73
65
  | `Tailwind::Icon` | Icon (SVG rendering, icon registry) |
66
+ | `Tailwind::Indicator` | Indicator (dot, pulse, badge) |
74
67
  | `Tailwind::Layout` | Layout primitives |
75
68
  | `Tailwind::Link` | Link |
76
69
  | `Tailwind::List` | List |
70
+ | `Tailwind::OrderedList` | Reorderable list (drag handle, item styling) |
71
+ | `Tailwind::Progress` | Progress bar, progress ring, meter |
77
72
  | `Tailwind::Timeline` | Timeline |
78
73
 
79
74
  Custom themes can subclass `TailwindTheme` to override individual methods, or subclass `StimulusPlumbers::Themes::Base` directly.
@@ -94,7 +89,7 @@ Icons render as inline `<svg>` with `size-6` by default. Override with `class:`:
94
89
  <%= sp_icon "arrow-left", class: "size-4 text-gray-500" %>
95
90
  ```
96
91
 
97
- **Built-in aliases:** `"close"` → `"x-mark"`, `"calendar"` → `"calendar-days"`.
92
+ **Built-in aliases:** `"close"` → `"x-mark"`, `"download"` → `"arrow-down-tray"`, `"book"` → `"book-open"`, `"edit"` → `"pencil"`, `"email"` → `"envelope"`, `"calendar"` → `"calendar-days"`, `"external-link"` → `"arrow-top-right-on-square"`, `"reveal"` → `"eye"`, `"grip-vertical"` → `"bars-3"`.
98
93
 
99
94
  **Optional `heroicons` gem:** add `gem "heroicons"` to your `Gemfile` to load icons from the gem instead of the bundled files. The theme detects it automatically.
100
95
 
@@ -0,0 +1,18 @@
1
+ @theme {
2
+ --animate-progress-slide: sp-progress-slide 1.5s ease-in-out infinite;
3
+ }
4
+
5
+ @keyframes sp-progress-slide {
6
+ 0% { transform: translateX(-100%); }
7
+ 100% { transform: translateX(300%); }
8
+ }
9
+
10
+ /* Ring-only; core's transition covers width, not stroke-dashoffset. */
11
+ [data-progress-target="fill"] {
12
+ transition: stroke-dashoffset 0.3s ease;
13
+ }
14
+
15
+ /* Track color — kept out of the SVG so consumers can retheme it via CSS. */
16
+ svg[role="progressbar"] .track {
17
+ stroke: var(--sp-color-muted);
18
+ }
data/docs/guide.md CHANGED
@@ -3,16 +3,27 @@
3
3
  Adding this gem registers and activates the Tailwind theme automatically in Rails — no config
4
4
  needed. Outside Rails, activate it explicitly with `config.theme.use(:tailwind)`.
5
5
 
6
- Run the install generator once to wire the required `@source` directive into your Tailwind CSS
7
- entry file:
6
+ Run the install generator once to wire the required CSS imports and `@source` directive into your
7
+ Tailwind CSS entry file:
8
8
 
9
9
  ```bash
10
10
  bin/rails generate stimulus_plumbers:tailwind:install
11
11
  ```
12
12
 
13
- It re-runs automatically on `assets:precompile`/`tailwindcss:build` after that see
14
- [README.md](../README.md#installation) for file-detection order and the `TAILWIND_CSS_FILE`
15
- override.
13
+ It re-runs automatically on `assets:precompile`/`tailwindcss:build` after that. File detection and
14
+ the `STIMULUS_PLUMBERS_CSS_ENTRY` override are shared with the core gem — see
15
+ [stimulus-plumbers-rails/docs/guide.md#css-entry-file-detection](../../stimulus-plumbers-rails/docs/guide.md#css-entry-file-detection).
16
+
17
+ ## Installed CSS files
18
+
19
+ The Tailwind installer copies these files into the application and imports them relatively:
20
+
21
+ - `app/assets/stylesheets/stimulus_plumbers/tokens.css`
22
+ - `app/assets/stylesheets/stimulus_plumbers/tailwind/animations.css` — registers the indeterminate progress bar slide keyframe.
23
+
24
+ Both files are application-owned: later generator runs restore a missing copy but never overwrite
25
+ an existing one. Legacy imports that point into gem directories are migrated to these app-local
26
+ paths.
16
27
 
17
28
  Icons: pass a kebab-case name to `sp_icon` or any `icon_leading:`/`icon_trailing:` option (append
18
29
  `/solid` for the filled variant) — see [README.md](../README.md#icons) for aliases and the
@@ -3,6 +3,8 @@
3
3
  require "rails/generators"
4
4
  require "stimulus_plumbers/generators/css_entrypoint"
5
5
  require "stimulus_plumbers/generators/tokens_directive"
6
+ require "stimulus_plumbers/tailwind/generators/animations_directive"
7
+ require "stimulus_plumbers/tailwind/generators/sources_directive"
6
8
 
7
9
  module StimulusPlumbers
8
10
  module Tailwind
@@ -10,52 +12,66 @@ module StimulusPlumbers
10
12
  class InstallGenerator < Rails::Generators::Base
11
13
  include StimulusPlumbers::Generators::CssEntrypoint
12
14
 
13
- GEM_NAME = "stimulus_plumbers_tailwind"
14
- LIB_DIR = File.expand_path("../../../..", __dir__)
15
- TAILWIND_CSS_FILE = "TAILWIND_CSS_FILE"
16
- CSS_CANDIDATES = %w[
17
- app/assets/stylesheets/application.tailwind.css
18
- app/assets/stylesheets/application.css
19
- app/javascript/entrypoints/application.css
20
- ].freeze
15
+ TOKENS_CSS_SOURCE = File.join(
16
+ Gem.loaded_specs.fetch("stimulus_plumbers").gem_dir,
17
+ StimulusPlumbers::Generators::TokensDirective::TOKENS_CSS_PATH
18
+ )
19
+ ANIMATIONS_CSS_SOURCE = File.join(
20
+ File.expand_path("../../../../../", __dir__),
21
+ AnimationsDirective::ANIMATIONS_CSS_PATH
22
+ )
21
23
 
22
24
  def install
23
25
  css_file = entry_css_file(**css_file_lookup_options)
24
26
  return warn_entry_css_not_found(label: "Tailwind CSS", **css_file_lookup_options) unless css_file
27
+ return unless copy_assets
25
28
 
26
- apply_edit(
27
- css_file,
28
- StimulusPlumbers::Generators::TokensDirective.directive,
29
- stale_pattern: StimulusPlumbers::Generators::TokensDirective.stale_pattern
30
- )
31
- apply_edit(
32
- css_file,
33
- source_directive,
34
- anchor_pattern: source_anchor_pattern,
35
- stale_pattern: source_stale_pattern
36
- )
29
+ apply_tokens_directive(css_file)
30
+ apply_animations_directive(css_file)
31
+ apply_sources_directive(css_file)
37
32
  end
38
33
 
39
34
  private
40
35
 
41
36
  def css_file_lookup_options
42
37
  {
43
- candidates: CSS_CANDIDATES,
44
- env_var: TAILWIND_CSS_FILE,
45
- fallback_env_var: StimulusPlumbers::Generators::CssEntrypoint::STIMULUS_PLUMBERS_CSS_FILE
38
+ candidates: StimulusPlumbers::Generators::CssEntrypoint::ENTRY_CANDIDATES,
39
+ env_var: StimulusPlumbers::Generators::CssEntrypoint::STIMULUS_PLUMBERS_CSS_ENTRY
46
40
  }
47
41
  end
48
42
 
49
- def source_directive
50
- %(@source "#{LIB_DIR}/**/*.rb";)
43
+ def apply_tokens_directive(css_file)
44
+ apply_edit(
45
+ css_file,
46
+ StimulusPlumbers::Generators::TokensDirective.directive(
47
+ from: File.dirname(css_file),
48
+ destination_root: destination_root
49
+ ),
50
+ stale_pattern: StimulusPlumbers::Generators::TokensDirective.stale_pattern
51
+ )
51
52
  end
52
53
 
53
- def source_anchor_pattern
54
- %r{@import "tailwindcss"[^;]*;}
54
+ def apply_animations_directive(css_file)
55
+ apply_edit(
56
+ css_file,
57
+ AnimationsDirective.directive(from: File.dirname(css_file), destination_root: destination_root),
58
+ anchor_pattern: AnimationsDirective.anchor_pattern,
59
+ stale_pattern: AnimationsDirective.stale_pattern
60
+ )
55
61
  end
56
62
 
57
- def source_stale_pattern
58
- %r{@source "[^"]*#{Regexp.escape(GEM_NAME)}[^"]*";}
63
+ def copy_assets
64
+ copy_asset(TOKENS_CSS_SOURCE, StimulusPlumbers::Generators::TokensDirective::TOKENS_CSS_PATH) &&
65
+ copy_asset(ANIMATIONS_CSS_SOURCE, AnimationsDirective::ANIMATIONS_CSS_PATH)
66
+ end
67
+
68
+ def apply_sources_directive(css_file)
69
+ apply_edit(
70
+ css_file,
71
+ SourcesDirective.directive(from: File.dirname(css_file)),
72
+ anchor_pattern: SourcesDirective.anchor_pattern,
73
+ stale_pattern: SourcesDirective.stale_pattern
74
+ )
59
75
  end
60
76
  end
61
77
  end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+
5
+ module StimulusPlumbers
6
+ module Tailwind
7
+ module Generators
8
+ module AnimationsDirective
9
+ ANIMATIONS_CSS_PATH = "app/assets/stylesheets/stimulus_plumbers/tailwind/animations.css"
10
+
11
+ module_function
12
+
13
+ def directive(from:, destination_root:)
14
+ path = Pathname.new(destination_root).join(ANIMATIONS_CSS_PATH)
15
+ rel = path.relative_path_from(Pathname.new(from))
16
+ rel = "./#{rel}" unless rel.to_s.start_with?(".", "/")
17
+ %(@import "#{rel}";)
18
+ end
19
+
20
+ # Anchors right after the core tokens.css import so the two asset
21
+ # imports stay grouped, ahead of the tailwindcss import and @source lines.
22
+ def anchor_pattern
23
+ %r{@import "[^"]*tokens\.css";}
24
+ end
25
+
26
+ def stale_pattern
27
+ %r{@import "[^"]*#{Regexp.escape(ANIMATIONS_CSS_PATH)}";}
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+
5
+ module StimulusPlumbers
6
+ module Tailwind
7
+ module Generators
8
+ module SourcesDirective
9
+ GEM_NAME = "stimulus_plumbers_tailwind"
10
+ LIB_DIR = File.expand_path("../../..", __dir__)
11
+
12
+ module_function
13
+
14
+ def directive(from:)
15
+ rel = Pathname.new(LIB_DIR).relative_path_from(Pathname.new(from))
16
+ %(@source "#{rel}/**/*.rb";)
17
+ end
18
+
19
+ def anchor_pattern
20
+ %r{@import "tailwindcss"[^;]*;}
21
+ end
22
+
23
+ def stale_pattern
24
+ %r{@source "[^"]*#{Regexp.escape(GEM_NAME)}[^"]*";}
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module StimulusPlumbers
4
4
  module Tailwind
5
- VERSION = "0.4.8"
5
+ VERSION = "0.4.10"
6
6
  end
7
7
  end
@@ -147,7 +147,7 @@ module StimulusPlumbers
147
147
  end
148
148
 
149
149
  def button_icon_classes
150
- { classes: klasses("size-(--sp-icon-size-sm)", "stroke-current") }
150
+ { classes: klasses("stroke-current") }
151
151
  end
152
152
  end
153
153
  end
@@ -20,24 +20,24 @@ module StimulusPlumbers
20
20
  info: %w[[--card-ring:var(--sp-color-info)]].freeze
21
21
  }.freeze
22
22
 
23
- HEADER_BASE = %w[
23
+ HEADER = %w[
24
24
  flex items-center gap-(--sp-space-3)
25
25
  px-(--sp-space-6) py-(--sp-space-6)
26
26
  ].freeze
27
27
 
28
- ICON_BASE = %w[
29
- size-(--sp-space-6) shrink-0 stroke-current text-(--sp-color-muted-fg)
28
+ ICON = %w[
29
+ shrink-0 stroke-current text-(--sp-color-muted-fg)
30
30
  ].freeze
31
31
 
32
- TITLE_BASE = %w[
32
+ TITLE = %w[
33
33
  text-(length:--sp-text-base) font-semibold text-(--sp-color-fg)
34
34
  ].freeze
35
35
 
36
- BODY_BASE = %w[
36
+ BODY = %w[
37
37
  px-(--sp-space-6) py-(--sp-space-3)
38
38
  ].freeze
39
39
 
40
- ACTION_BASE = %w[w-full justify-start].freeze
40
+ ACTION = %w[w-full justify-start].freeze
41
41
 
42
42
  private
43
43
 
@@ -45,11 +45,11 @@ module StimulusPlumbers
45
45
  { classes: klasses(*VARIANTS.fetch(variant, VARIANTS[:tertiary]), *BASE) }
46
46
  end
47
47
 
48
- def card_header_classes = { classes: klasses(*HEADER_BASE) }
49
- def card_icon_classes = { classes: klasses(*ICON_BASE) }
50
- def card_title_classes = { classes: klasses(*TITLE_BASE) }
51
- def card_body_classes = { classes: klasses(*BODY_BASE) }
52
- def card_action_classes = { classes: klasses(*ACTION_BASE) }
48
+ def card_header_classes = { classes: klasses(*HEADER) }
49
+ def card_icon_classes = { classes: klasses(*ICON) }
50
+ def card_title_classes = { classes: klasses(*TITLE) }
51
+ def card_body_classes = { classes: klasses(*BODY) }
52
+ def card_action_classes = { classes: klasses(*ACTION) }
53
53
  end
54
54
  end
55
55
  end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StimulusPlumbers
4
+ module Themes
5
+ module Tailwind
6
+ module Checklist
7
+ CHECKLIST = %w[flex flex-col gap-(--sp-space-1)].freeze
8
+
9
+ ITEM = [
10
+ *Control::BASE,
11
+ "group/checklist-item flex items-center gap-(--sp-space-2) w-full text-start",
12
+ "px-(--sp-space-2) py-(--sp-space-1)",
13
+ "rounded-(--sp-radius-sm) text-(length:--sp-text-sm)",
14
+ "cursor-pointer select-none text-(--sp-color-fg)",
15
+ "hover:bg-(--sp-color-muted)",
16
+ "has-disabled:cursor-default has-disabled:hover:bg-transparent"
17
+ ].freeze
18
+
19
+ INPUT = [
20
+ *Control::ACCENT,
21
+ "size-(--sp-control-size) rounded-(--sp-radius-sm) shrink-0",
22
+ "border border-(--sp-color-border) bg-(--sp-color-muted)",
23
+ "focus:ring-(length:--sp-focus-ring-width) focus:ring-(--sp-focus-ring-color) focus:outline-none",
24
+ "disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer"
25
+ ].freeze
26
+
27
+ CONTENT = %w[flex flex-col flex-1 min-w-0].freeze
28
+
29
+ TITLE = %w[
30
+ text-(length:--sp-text-sm) font-medium text-(--sp-color-fg)
31
+ group-has-checked/checklist-item:line-through
32
+ group-has-checked/checklist-item:text-(--sp-color-muted-fg)
33
+ ].freeze
34
+
35
+ DESCRIPTION = %w[text-(length:--sp-text-xs) text-(--sp-color-muted-fg)].freeze
36
+
37
+ private
38
+
39
+ def checklist_classes
40
+ { classes: klasses(*CHECKLIST) }
41
+ end
42
+
43
+ def checklist_item_classes
44
+ { classes: klasses(*ITEM) }
45
+ end
46
+
47
+ def checklist_item_input_classes
48
+ { classes: klasses(*INPUT) }
49
+ end
50
+
51
+ def checklist_item_content_classes
52
+ { classes: klasses(*CONTENT) }
53
+ end
54
+
55
+ def checklist_item_title_classes
56
+ { classes: klasses(*TITLE) }
57
+ end
58
+
59
+ def checklist_item_description_classes
60
+ { classes: klasses(*DESCRIPTION) }
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -32,7 +32,7 @@ module StimulusPlumbers
32
32
  py-(--sp-space-1) overflow-y-auto max-h-60 min-h-(--sp-space-6)
33
33
  ].freeze
34
34
 
35
- OPTION_BASE = %w[
35
+ OPTION = %w[
36
36
  flex items-center gap-(--sp-space-2) w-full
37
37
  px-(--sp-space-2) py-(--sp-space-1)
38
38
  rounded-(--sp-radius-sm) text-(length:--sp-text-sm)
@@ -96,7 +96,7 @@ module StimulusPlumbers
96
96
  def combobox_option_classes(selected: false, disabled: false)
97
97
  {
98
98
  classes: klasses(
99
- *OPTION_BASE,
99
+ *OPTION,
100
100
  *(selected ? OPTION_SELECTED : []),
101
101
  *(disabled ? OPTION_DISABLED : [])
102
102
  )
@@ -112,7 +112,7 @@ module StimulusPlumbers
112
112
  end
113
113
 
114
114
  def combobox_typeahead_loading_icon_classes
115
- { classes: klasses("size-(--sp-icon-size-md)", "animate-spin") }
115
+ { classes: klasses("animate-spin") }
116
116
  end
117
117
 
118
118
  def combobox_typeahead_empty_classes
@@ -11,6 +11,8 @@ module StimulusPlumbers
11
11
  focus-visible:ring-offset-(length:--sp-focus-ring-offset)
12
12
  disabled:pointer-events-none disabled:opacity-50
13
13
  ].freeze
14
+
15
+ ACCENT = %w[[accent-color:var(--sp-color-primary)] cursor-pointer].freeze
14
16
  end
15
17
  end
16
18
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StimulusPlumbers
4
+ module Themes
5
+ module Tailwind
6
+ module Form
7
+ module Code
8
+ FIELD = %w[
9
+ relative inline-flex w-fit rounded-(--sp-radius-md)
10
+ focus-within:ring-(length:--sp-focus-ring-width) focus-within:ring-(--sp-focus-ring-color)
11
+ focus-within:ring-offset-(length:--sp-focus-ring-offset)
12
+ ].freeze
13
+ FIELD_ERROR = %w[focus-within:ring-(--sp-color-error)].freeze
14
+ CELLS = %w[flex items-center gap-(--sp-space-1)].freeze
15
+ CELL = %w[
16
+ flex size-10 items-center justify-center rounded-(--sp-radius-md) border
17
+ border-(--sp-color-muted-fg) bg-(--sp-color-bg)
18
+ text-(length:--sp-text-lg) font-medium text-(--sp-color-fg)
19
+ transition-colors
20
+ data-[filled]:border-(--sp-color-fg)
21
+ data-[caret]:border-(--sp-color-primary) data-[caret]:ring-1 data-[caret]:ring-(--sp-color-primary)
22
+ data-[group-end]:me-(--sp-space-2)
23
+ ].freeze
24
+ CELL_ERROR = %w[border-(--sp-color-error) data-[caret]:ring-(--sp-color-error)].freeze
25
+ OVERLAY = %w[absolute inset-0 size-full cursor-text opacity-0 disabled:cursor-not-allowed].freeze
26
+
27
+ private
28
+
29
+ def form_field_input_code_classes(error: false) = { classes: klasses(*FIELD, *(error ? FIELD_ERROR : [])) }
30
+ def form_field_input_code_cells_classes = { classes: klasses(*CELLS) }
31
+ def form_field_input_code_cell_classes(error: false) = { classes: klasses(*CELL, *(error ? CELL_ERROR : [])) }
32
+ def form_field_input_code_overlay_classes(**) = { classes: klasses(*OVERLAY) }
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StimulusPlumbers
4
+ module Themes
5
+ module Tailwind
6
+ module Form
7
+ module CreditCard
8
+ FIELD = %w[
9
+ relative inline-flex w-fit rounded-(--sp-radius-md)
10
+ focus-within:ring-(length:--sp-focus-ring-width) focus-within:ring-(--sp-focus-ring-color)
11
+ focus-within:ring-offset-(length:--sp-focus-ring-offset)
12
+ ].freeze
13
+ FIELD_ERROR = %w[focus-within:ring-(--sp-color-error)].freeze
14
+ CELLS = %w[flex items-center gap-(--sp-space-1)].freeze
15
+ CELL = %w[
16
+ flex h-10 min-w-16 items-center justify-center px-(--sp-space-2)
17
+ rounded-(--sp-radius-md) border
18
+ border-(--sp-color-muted-fg) bg-(--sp-color-bg)
19
+ text-(length:--sp-text-lg) font-medium text-(--sp-color-fg)
20
+ transition-colors
21
+ data-[filled]:border-(--sp-color-fg)
22
+ data-[caret]:border-(--sp-color-primary) data-[caret]:ring-1 data-[caret]:ring-(--sp-color-primary)
23
+ ].freeze
24
+ CELL_ERROR = %w[border-(--sp-color-error) data-[caret]:ring-(--sp-color-error)].freeze
25
+ SEPARATOR = %w[text-(--sp-color-muted-fg)].freeze
26
+ OVERLAY = %w[absolute inset-0 size-full cursor-text opacity-0 disabled:cursor-not-allowed].freeze
27
+
28
+ private
29
+
30
+ def form_field_input_credit_card_classes(error: false) = { classes: klasses(*FIELD, *(error ? FIELD_ERROR : [])) }
31
+ def form_field_input_credit_card_cells_classes = { classes: klasses(*CELLS) }
32
+ def form_field_input_credit_card_cell_classes(error: false) = { classes: klasses(*CELL, *(error ? CELL_ERROR : [])) }
33
+ def form_field_input_credit_card_separator_classes = { classes: klasses(*SEPARATOR) }
34
+ def form_field_input_credit_card_overlay_classes(**) = { classes: klasses(*OVERLAY) }
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -16,7 +16,7 @@ module StimulusPlumbers
16
16
  standard: %w[relative z-0].freeze
17
17
  }.freeze
18
18
 
19
- FLOATING_LABEL_BASE = %w[
19
+ FLOATING_LABEL = %w[
20
20
  absolute text-(length:--sp-text-sm) text-(--sp-color-muted-fg)
21
21
  duration-300 transform origin-[0]
22
22
  ].freeze
@@ -117,7 +117,7 @@ module StimulusPlumbers
117
117
  def form_field_label_classes(floating: nil, hidden: false, error: false, **)
118
118
  if floating
119
119
  color = error ? FLOATING_LABEL_ERROR : FLOATING_LABEL_FOCUS
120
- { classes: klasses(*FLOATING_LABEL_BASE, *FLOATING_LABEL_TYPES.fetch(floating, []), *color) }
120
+ { classes: klasses(*FLOATING_LABEL, *FLOATING_LABEL_TYPES.fetch(floating, []), *color) }
121
121
  else
122
122
  { classes: klasses(*LABEL, hidden ? "sr-only" : nil) }
123
123
  end
@@ -6,7 +6,7 @@ module StimulusPlumbers
6
6
  module Form
7
7
  module Input
8
8
  # ── Standalone input ──────────────────────────────────────────────────
9
- INPUT_BASE = %w[
9
+ INPUT = %w[
10
10
  w-full rounded-(--sp-radius-md) border px-(--sp-space-3) py-(--sp-space-2)
11
11
  text-(length:--sp-text-sm) text-(--sp-color-fg) bg-(--sp-color-bg)
12
12
  focus:outline-none focus:ring-(length:--sp-focus-ring-width) focus:ring-offset-0
@@ -18,7 +18,7 @@ module StimulusPlumbers
18
18
  INPUT_ERROR = %w[border-(--sp-color-error) focus:ring-(--sp-color-error)].freeze
19
19
 
20
20
  # ── Floating input ────────────────────────────────────────────────────
21
- FLOATING_INPUT_BASE = %w[
21
+ FLOATING_INPUT = %w[
22
22
  peer w-full text-(length:--sp-text-sm) text-(--sp-color-fg) appearance-none
23
23
  focus:outline-none focus:ring-0
24
24
  focus-visible:outline-none focus-visible:ring-0
@@ -44,11 +44,11 @@ module StimulusPlumbers
44
44
  FLOATING_INPUT_ERROR = %w[border-(--sp-color-error)].freeze
45
45
 
46
46
  # ── Input group ───────────────────────────────────────────────────────
47
- INPUT_GROUP_BASE = %w[flex items-center overflow-hidden rounded-(--sp-radius-md) border].freeze
47
+ INPUT_GROUP = %w[flex items-center overflow-hidden rounded-(--sp-radius-md) border].freeze
48
48
  INPUT_GROUP_BORDER = { error: "border-(--sp-color-error)", default: "border-(--sp-color-muted-fg)" }.freeze
49
49
 
50
50
  # ── Floating input group ──────────────────────────────────────────────
51
- FLOATING_INPUT_GROUP_BASE = %w[flex items-center overflow-hidden peer].freeze
51
+ FLOATING_INPUT_GROUP = %w[flex items-center overflow-hidden peer].freeze
52
52
  FLOATING_INPUT_GROUP_TYPES = {
53
53
  filled: %w[rounded-t-(--sp-radius-md) bg-(--sp-color-bg-muted) border-0 border-b-2].freeze,
54
54
  outlined: %w[rounded-(--sp-radius-md) border].freeze,
@@ -80,21 +80,24 @@ module StimulusPlumbers
80
80
 
81
81
  # ── Choice inputs ─────────────────────────────────────────────────────
82
82
  CHECKBOX_TYPES = {
83
- default: %w[
84
- size-(--sp-control-size) rounded-(--sp-radius-sm) shrink-0
85
- border border-(--sp-color-border) bg-(--sp-color-muted)
86
- focus:ring-(length:--sp-focus-ring-width) focus:ring-(--sp-focus-ring-color) focus:outline-none
87
- disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer
83
+ default: [
84
+ *Control::ACCENT,
85
+ "size-(--sp-control-size) rounded-(--sp-radius-sm) shrink-0",
86
+ "border border-(--sp-color-border) bg-(--sp-color-muted)",
87
+ "focus:ring-(length:--sp-focus-ring-width) focus:ring-(--sp-focus-ring-color) focus:outline-none",
88
+ "disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer"
88
89
  ].freeze,
89
- button: %w[
90
- size-(--sp-control-size) rounded-(--sp-radius-sm) shrink-0
91
- border border-(--sp-color-border) bg-(--sp-color-muted)
92
- focus:ring-(length:--sp-focus-ring-width) focus:ring-(--sp-focus-ring-color) focus:outline-none
93
- disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer
90
+ button: [
91
+ *Control::ACCENT,
92
+ "size-(--sp-control-size) rounded-(--sp-radius-sm) shrink-0",
93
+ "border border-(--sp-color-border) bg-(--sp-color-muted)",
94
+ "focus:ring-(length:--sp-focus-ring-width) focus:ring-(--sp-focus-ring-color) focus:outline-none",
95
+ "disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer"
94
96
  ].freeze,
95
97
  card: %w[
96
98
  size-(--sp-control-size) rounded-(--sp-radius-sm) shrink-0
97
99
  border border-(--sp-color-border) bg-(--sp-color-muted)
100
+ [accent-color:var(--card-ring)] cursor-pointer
98
101
  checked:border-(--card-ring)
99
102
  focus:ring-(length:--sp-focus-ring-width) focus:ring-(--card-ring) focus:outline-none
100
103
  disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer
@@ -102,11 +105,11 @@ module StimulusPlumbers
102
105
  }.freeze
103
106
 
104
107
  RADIO_TYPES = {
105
- default: %w[
106
- size-(--sp-control-size) rounded-full shrink-0
107
- [accent-color:var(--sp-color-primary)] cursor-pointer
108
- focus:ring-(length:--sp-focus-ring-width) focus:ring-(--sp-focus-ring-color) focus:outline-none
109
- disabled:opacity-50 disabled:cursor-not-allowed
108
+ default: [
109
+ *Control::ACCENT,
110
+ "size-(--sp-control-size) rounded-full shrink-0",
111
+ "focus:ring-(length:--sp-focus-ring-width) focus:ring-(--sp-focus-ring-color) focus:outline-none",
112
+ "disabled:opacity-50 disabled:cursor-not-allowed"
110
113
  ].freeze,
111
114
  button: %w[hidden].freeze,
112
115
  card: %w[hidden].freeze
@@ -133,13 +136,13 @@ module StimulusPlumbers
133
136
  def form_field_input_classes(floating: nil, error: false)
134
137
  if floating
135
138
  { classes: klasses(
136
- *FLOATING_INPUT_BASE,
139
+ *FLOATING_INPUT,
137
140
  *FLOATING_INPUT_TYPES.fetch(floating, []),
138
141
  *(error ? FLOATING_INPUT_ERROR : FLOATING_INPUT_DEFAULT)
139
142
  )
140
143
  }
141
144
  else
142
- { classes: klasses(*INPUT_BASE, *(error ? INPUT_ERROR : INPUT_DEFAULT)) }
145
+ { classes: klasses(*INPUT, *(error ? INPUT_ERROR : INPUT_DEFAULT)) }
143
146
  end
144
147
  end
145
148
 
@@ -168,9 +171,9 @@ module StimulusPlumbers
168
171
  def input_group_classes(error: false, floating: nil)
169
172
  if floating
170
173
  color = error ? FLOATING_INPUT_GROUP_ERROR : FLOATING_INPUT_GROUP_DEFAULT
171
- { classes: klasses(*FLOATING_INPUT_GROUP_BASE, *FLOATING_INPUT_GROUP_TYPES.fetch(floating, []), *color) }
174
+ { classes: klasses(*FLOATING_INPUT_GROUP, *FLOATING_INPUT_GROUP_TYPES.fetch(floating, []), *color) }
172
175
  else
173
- { classes: klasses(*INPUT_GROUP_BASE, INPUT_GROUP_BORDER[error ? :error : :default]) }
176
+ { classes: klasses(*INPUT_GROUP, INPUT_GROUP_BORDER[error ? :error : :default]) }
174
177
  end
175
178
  end
176
179
 
@@ -180,7 +183,7 @@ module StimulusPlumbers
180
183
  else
181
184
  {
182
185
  classes: klasses(
183
- *INPUT_BASE,
186
+ *INPUT,
184
187
  *(error ? INPUT_ERROR : INPUT_DEFAULT),
185
188
  *COMBOBOX_INPUT,
186
189
  *COMBOBOX_TRIGGER_GROUP
@@ -192,7 +195,7 @@ module StimulusPlumbers
192
195
  def form_field_input_combobox_floating_classes(floating: :standard, error: false)
193
196
  {
194
197
  classes: klasses(
195
- *FLOATING_INPUT_BASE,
198
+ *FLOATING_INPUT,
196
199
  *FLOATING_INPUT_TYPES.fetch(floating, []),
197
200
  *(error ? FLOATING_INPUT_ERROR : FLOATING_INPUT_DEFAULT),
198
201
  *COMBOBOX_INPUT,
@@ -4,13 +4,13 @@ module StimulusPlumbers
4
4
  module Themes
5
5
  module Tailwind
6
6
  module Form
7
- GROUP_BASE = %w[flex gap-(--sp-space-1) mb-(--sp-space-3)].freeze
7
+ GROUP = %w[flex gap-(--sp-space-1) mb-(--sp-space-3)].freeze
8
8
  GROUP_INLINE = %w[flex-row items-center].freeze
9
9
 
10
10
  private
11
11
 
12
12
  def form_group_classes(layout: :stacked, **_rest)
13
- { classes: klasses(*GROUP_BASE, layout == :inline ? GROUP_INLINE : "flex-col") }
13
+ { classes: klasses(*GROUP, layout == :inline ? GROUP_INLINE : "flex-col") }
14
14
  end
15
15
 
16
16
  def form_submit_classes(**) = {}
@@ -16,7 +16,8 @@ module StimulusPlumbers
16
16
  "email" => "envelope",
17
17
  "calendar" => "calendar-days",
18
18
  "external-link" => "arrow-top-right-on-square",
19
- "reveal" => "eye"
19
+ "reveal" => "eye",
20
+ "grip-vertical" => "bars-3"
20
21
  }.freeze
21
22
 
22
23
  ICONS = StimulusPlumbers::Themes::Icons::Registry.new(
@@ -24,14 +25,20 @@ module StimulusPlumbers
24
25
  aliases: ALIASES
25
26
  )
26
27
 
28
+ SIZES = {
29
+ sm: "size-(--sp-icon-size-sm)",
30
+ md: "size-(--sp-icon-size-md)",
31
+ lg: "size-(--sp-icon-size-lg)"
32
+ }.freeze
33
+
27
34
  def icons
28
35
  ICONS
29
36
  end
30
37
 
31
38
  private
32
39
 
33
- def icon_classes
34
- { classes: "size-(--sp-icon-size-lg)" }
40
+ def icon_classes(size: :lg)
41
+ { classes: SIZES.fetch(size, SIZES[:lg]) }
35
42
  end
36
43
  end
37
44
  end
@@ -0,0 +1,12 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 44 44" width="44" height="44" fill="none">
2
+ <circle cx="22" cy="22" r="18" stroke-width="3" class="track"></circle>
3
+ <circle
4
+ cx="22"
5
+ cy="22"
6
+ r="18"
7
+ stroke="currentColor"
8
+ stroke-width="3"
9
+ stroke-linecap="round"
10
+ data-progress-target="fill"
11
+ ></circle>
12
+ </svg>
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StimulusPlumbers
4
+ module Themes
5
+ module Tailwind
6
+ module Indicator
7
+ VARIANTS = {
8
+ primary: "bg-(--sp-color-primary)",
9
+ secondary: "bg-(--sp-color-secondary)",
10
+ tertiary: "bg-(--sp-color-muted-fg)",
11
+ success: "bg-(--sp-color-success)",
12
+ destructive: "bg-(--sp-color-destructive)",
13
+ warning: "bg-(--sp-color-warning)",
14
+ info: "bg-(--sp-color-info)"
15
+ }.freeze
16
+
17
+ DOT = %w[inline-flex size-2.5 rounded-full].freeze
18
+ BADGE = %w[
19
+ inline-flex items-center justify-center
20
+ min-w-5 h-5 px-1
21
+ rounded-full text-(length:--sp-text-xs) font-medium text-white
22
+ ].freeze
23
+
24
+ # Establishes the positioning context the pulse ring overlays onto — no margin needed.
25
+ WRAPPER = %w[relative inline-flex items-center justify-center].freeze
26
+
27
+ PULSE_RING = %w[
28
+ absolute inset-0 rounded-full
29
+ bg-(--sp-color-indicator) opacity-75
30
+ animate-ping motion-reduce:animate-none
31
+ ].freeze
32
+
33
+ private
34
+
35
+ def indicator_classes(type: :dot, variant: nil)
36
+ { classes: klasses(*(type.to_sym == :badge ? BADGE : DOT), VARIANTS.fetch(variant, nil)) }
37
+ end
38
+
39
+ def indicator_wrapper_classes
40
+ { classes: klasses(*WRAPPER) }
41
+ end
42
+
43
+ def indicator_pulse_classes
44
+ { classes: klasses(*PULSE_RING) }
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -79,7 +79,7 @@ module StimulusPlumbers
79
79
  end
80
80
 
81
81
  def link_icon_classes
82
- { classes: klasses("size-(--sp-icon-size-sm)", "stroke-current") }
82
+ { classes: klasses("stroke-current") }
83
83
  end
84
84
  end
85
85
  end
@@ -5,19 +5,19 @@ module StimulusPlumbers
5
5
  module Tailwind
6
6
  module List
7
7
  # ── Section ───────────────────────────────────────────────────────────
8
- SECTION_TITLE_BASE = %w[
8
+ SECTION_TITLE = %w[
9
9
  block px-(--sp-space-2) pb-(--sp-space-1)
10
10
  text-(length:--sp-text-xs) font-semibold uppercase tracking-wider
11
11
  text-(--sp-color-muted-fg)
12
12
  ].freeze
13
13
 
14
- SECTION_DESCRIPTION_BASE = %w[
14
+ SECTION_DESCRIPTION = %w[
15
15
  block px-(--sp-space-2) pb-(--sp-space-1)
16
16
  text-(length:--sp-text-xs) text-(--sp-color-muted-fg)
17
17
  ].freeze
18
18
 
19
19
  # ── Item ──────────────────────────────────────────────────────────────
20
- ITEM_BASE = [
20
+ ITEM = [
21
21
  *Control::BASE,
22
22
  "flex items-center gap-(--sp-space-2) w-full",
23
23
  "px-(--sp-space-2) py-(--sp-space-1)",
@@ -29,15 +29,15 @@ module StimulusPlumbers
29
29
  "aria-[current]:bg-(--sp-color-primary)/10 aria-[current]:text-(--sp-color-primary)"
30
30
  ].freeze
31
31
 
32
- ITEM_CONTENT_BASE = %w[
32
+ ITEM_CONTENT = %w[
33
33
  flex flex-col flex-1 min-w-0
34
34
  ].freeze
35
35
 
36
- ITEM_TITLE_BASE = %w[
36
+ ITEM_TITLE = %w[
37
37
  text-(length:--sp-text-sm) font-medium text-(--sp-color-fg)
38
38
  ].freeze
39
39
 
40
- ITEM_DESCRIPTION_BASE = %w[
40
+ ITEM_DESCRIPTION = %w[
41
41
  text-(length:--sp-text-xs) text-(--sp-color-muted-fg)
42
42
  ].freeze
43
43
 
@@ -52,31 +52,31 @@ module StimulusPlumbers
52
52
  end
53
53
 
54
54
  def list_section_title_classes
55
- { classes: klasses(*SECTION_TITLE_BASE) }
55
+ { classes: klasses(*SECTION_TITLE) }
56
56
  end
57
57
 
58
58
  def list_section_description_classes
59
- { classes: klasses(*SECTION_DESCRIPTION_BASE) }
59
+ { classes: klasses(*SECTION_DESCRIPTION) }
60
60
  end
61
61
 
62
62
  def list_item_classes
63
- { classes: klasses(*ITEM_BASE) }
63
+ { classes: klasses(*ITEM) }
64
64
  end
65
65
 
66
66
  def list_item_icon_classes
67
- { classes: klasses("size-(--sp-icon-size-sm)", "stroke-current") }
67
+ { classes: klasses("stroke-current") }
68
68
  end
69
69
 
70
70
  def list_item_content_classes
71
- { classes: klasses(*ITEM_CONTENT_BASE) }
71
+ { classes: klasses(*ITEM_CONTENT) }
72
72
  end
73
73
 
74
74
  def list_item_title_classes
75
- { classes: klasses(*ITEM_TITLE_BASE) }
75
+ { classes: klasses(*ITEM_TITLE) }
76
76
  end
77
77
 
78
78
  def list_item_description_classes
79
- { classes: klasses(*ITEM_DESCRIPTION_BASE) }
79
+ { classes: klasses(*ITEM_DESCRIPTION) }
80
80
  end
81
81
  end
82
82
  end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StimulusPlumbers
4
+ module Themes
5
+ module Tailwind
6
+ module OrderedList
7
+ # ── Item ──────────────────────────────────────────────────────────────
8
+ ITEM = [
9
+ *Control::BASE,
10
+ "flex items-center gap-(--sp-space-2) w-full",
11
+ "px-(--sp-space-2) py-(--sp-space-1)",
12
+ "rounded-(--sp-radius-sm) text-(length:--sp-text-sm)",
13
+ "text-(--sp-color-fg)",
14
+ "focus-visible:ring-(--sp-color-primary-ring)",
15
+ "aria-[current]:bg-(--sp-color-primary)/10 aria-[current]:text-(--sp-color-primary)"
16
+ ].freeze
17
+
18
+ HANDLE = %w[
19
+ flex items-center justify-center shrink-0
20
+ stroke-current
21
+ text-(--sp-color-muted-fg)
22
+ cursor-grab active:cursor-grabbing
23
+ touch-none
24
+ ].freeze
25
+
26
+ CONTENT = %w[
27
+ flex flex-col flex-1 min-w-0
28
+ ].freeze
29
+
30
+ TITLE = %w[
31
+ text-(length:--sp-text-sm) font-medium text-(--sp-color-fg)
32
+ ].freeze
33
+
34
+ DESCRIPTION = %w[
35
+ text-(length:--sp-text-xs) text-(--sp-color-muted-fg)
36
+ ].freeze
37
+
38
+ private
39
+
40
+ def ordered_list_classes
41
+ { classes: klasses("flex flex-col gap-(--sp-space-1) py-(--sp-space-1)") }
42
+ end
43
+
44
+ def ordered_list_item_classes
45
+ { classes: klasses(*ITEM) }
46
+ end
47
+
48
+ def ordered_list_item_handle_classes
49
+ { classes: klasses(*HANDLE) }
50
+ end
51
+
52
+ def ordered_list_item_content_classes
53
+ { classes: klasses(*CONTENT) }
54
+ end
55
+
56
+ def ordered_list_item_title_classes
57
+ { classes: klasses(*TITLE) }
58
+ end
59
+
60
+ def ordered_list_item_description_classes
61
+ { classes: klasses(*DESCRIPTION) }
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StimulusPlumbers
4
+ module Themes
5
+ module Tailwind
6
+ module Progress
7
+ BAR = %w[
8
+ relative w-full h-2 overflow-hidden rounded-full
9
+ bg-(--sp-color-muted)
10
+ ].freeze
11
+
12
+ BAR_FILL = %w[
13
+ h-full rounded-full bg-(--sp-color-primary)
14
+ [.sp-progress-indeterminate_&]:animate-progress-slide
15
+ [.sp-progress-indeterminate_&]:motion-reduce:animate-none
16
+ ].freeze
17
+
18
+ # Circle stroke/fill live in icons/customs/progress-ring.svg — @source never scans
19
+ # .svg files, so Tailwind classes there are inert.
20
+ # Indeterminate class and utility land on this same svg, so this needs the compound
21
+ # [&.foo] form, not BAR_FILL's [.foo_&] descendant form.
22
+ RING = %w[
23
+ -rotate-90 text-(--sp-color-primary)
24
+ [&.sp-progress-indeterminate]:animate-spin
25
+ ].freeze
26
+
27
+ METER = %w[
28
+ w-full h-2 rounded-full
29
+ [&::-webkit-meter-bar]:rounded-full [&::-webkit-meter-bar]:bg-(--sp-color-muted)
30
+ [&::-webkit-meter-optimum-value]:bg-(--sp-color-success)
31
+ [&::-webkit-meter-suboptimum-value]:bg-(--sp-color-warning)
32
+ [&::-webkit-meter-even-less-good-value]:bg-(--sp-color-destructive)
33
+ [&::-moz-meter-bar]:bg-(--sp-color-success)
34
+ ].freeze
35
+
36
+ private
37
+
38
+ def progress_bar_classes = { classes: klasses(*BAR) }
39
+ def progress_bar_fill_classes = { classes: klasses(*BAR_FILL) }
40
+ def progress_ring_classes = { classes: klasses(*RING) }
41
+ def progress_meter_classes = { classes: klasses(*METER) }
42
+ end
43
+ end
44
+ end
45
+ end
@@ -5,22 +5,31 @@ module StimulusPlumbers
5
5
  module Tailwind
6
6
  module Timeline
7
7
  module Group
8
- WRAPPER = %w[space-y-4].freeze
9
- SECTION = %w[
8
+ WRAPPER = %w[flex flex-col gap-y-4].freeze
9
+ SECTION = %w[
10
10
  p-4
11
11
  bg-(--sp-color-bg-muted)
12
12
  border border-(--sp-color-border)
13
13
  rounded-lg
14
14
  ].freeze
15
- DATE = %w[text-base font-semibold text-(--sp-color-fg)].freeze
16
- LIST = %w[mt-3 divide-y divide-(--sp-color-border)].freeze
15
+ DATE = %w[text-base font-semibold text-(--sp-color-fg)].freeze
16
+ LIST_VERTICAL = %w[
17
+ pt-3 flex flex-col
18
+ divide-y divide-(--sp-color-border)
19
+ [&>li]:py-2 [&>li]:first:pt-0 [&>li]:last:pb-0
20
+ ].freeze
21
+ LIST_HORIZONTAL = %w[pt-3 flex gap-x-4].freeze
17
22
 
18
23
  private
19
24
 
20
25
  def timeline_group_classes = { classes: klasses(WRAPPER) }
21
26
  def timeline_group_section_classes = { classes: klasses(SECTION) }
22
27
  def timeline_group_section_date_classes = { classes: klasses(DATE) }
23
- def timeline_group_section_list_classes = { classes: klasses(LIST) }
28
+
29
+ def timeline_group_section_list_classes(orientation: :vertical)
30
+ list = orientation.to_sym == :horizontal ? LIST_HORIZONTAL : LIST_VERTICAL
31
+ { classes: klasses(list) }
32
+ end
24
33
  end
25
34
  end
26
35
  end
@@ -18,7 +18,6 @@ module StimulusPlumbers
18
18
  mt-1 flex shrink-0 size-6 items-center justify-center rounded-full z-10
19
19
  bg-(--sp-color-bg) ring-4 ring-(--sp-color-bg)
20
20
  ].freeze
21
- ITEM_INDICATOR_DOT_MARK = %w[size-3 rounded-full bg-(--sp-color-indicator)].freeze
22
21
 
23
22
  ITEM_INDICATOR_ICON = %w[
24
23
  mt-1 flex shrink-0 size-6 items-center justify-center rounded-full z-10
@@ -26,7 +25,7 @@ module StimulusPlumbers
26
25
  ring-4 ring-(--sp-color-bg)
27
26
  ].freeze
28
27
 
29
- ITEM_INDICATOR_ICON_SLOT = %w[size-(--sp-icon-size-sm) stroke-current].freeze
28
+ ITEM_INDICATOR_ICON_SLOT = %w[stroke-current].freeze
30
29
 
31
30
  ITEM_INDICATOR_DOT_HORIZONTAL = %w[
32
31
  z-10 flex shrink-0 size-6 items-center justify-center rounded-full
@@ -50,12 +49,12 @@ module StimulusPlumbers
50
49
  ].freeze
51
50
  ITEM_TITLE = %w[mb-1 text-base font-semibold text-(--sp-color-fg)].freeze
52
51
  ITEM_HEADING = %w[mb-1].freeze
53
- ITEM_TRIGGER = %w[
54
- w-full text-left text-base font-semibold
55
- text-(--sp-color-fg)
56
- hover:text-(--sp-color-primary)
57
- focus-visible:outline-none focus-visible:ring-2
58
- focus-visible:ring-(--sp-color-primary) focus-visible:rounded-sm
52
+ ITEM_TRIGGER = [
53
+ *Control::BASE,
54
+ "w-full text-left text-base font-semibold",
55
+ "text-(--sp-color-fg)",
56
+ "hover:text-(--sp-color-primary)",
57
+ "focus-visible:ring-(--sp-color-primary-ring) focus-visible:rounded-sm"
59
58
  ].freeze
60
59
  ITEM_DESCRIPTION = %w[text-sm text-(--sp-color-muted-fg)].freeze
61
60
  ITEM_DETAIL = %w[mt-2 text-sm text-(--sp-color-muted-fg)].freeze
@@ -93,7 +92,6 @@ module StimulusPlumbers
93
92
  def timeline_item_description_classes = { classes: klasses(ITEM_DESCRIPTION) }
94
93
  def timeline_item_detail_classes = { classes: klasses(ITEM_DETAIL) }
95
94
  def timeline_item_actions_classes = { classes: klasses(ITEM_ACTIONS) }
96
- def timeline_item_indicator_dot_classes = { classes: klasses(ITEM_INDICATOR_DOT_MARK) }
97
95
  def timeline_item_connector_classes = { classes: klasses(ITEM_CONNECTOR_HORIZONTAL) }
98
96
  def timeline_item_content_classes = { classes: klasses(ITEM_CONTENT_HORIZONTAL) }
99
97
  def timeline_track_line_classes = { classes: klasses(TRACK_LINE_VERTICAL) }
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "tailwind/control"
4
+ require_relative "tailwind/checklist"
4
5
  require_relative "tailwind/list"
6
+ require_relative "tailwind/ordered_list"
5
7
  require_relative "tailwind/avatar"
6
8
  require_relative "tailwind/button"
7
9
  require_relative "tailwind/button/group"
@@ -10,16 +12,22 @@ require_relative "tailwind/card"
10
12
  require_relative "tailwind/combobox"
11
13
  require_relative "tailwind/form"
12
14
  require_relative "tailwind/form/field"
15
+ require_relative "tailwind/form/code"
16
+ require_relative "tailwind/form/credit_card"
13
17
  require_relative "tailwind/form/input"
14
18
  require_relative "tailwind/icon"
19
+ require_relative "tailwind/indicator"
15
20
  require_relative "tailwind/layout"
16
21
  require_relative "tailwind/link"
22
+ require_relative "tailwind/progress"
17
23
  require_relative "tailwind/timeline"
18
24
 
19
25
  module StimulusPlumbers
20
26
  module Themes
21
27
  class TailwindTheme < Base
28
+ include Tailwind::Checklist
22
29
  include Tailwind::List
30
+ include Tailwind::OrderedList
23
31
  include Tailwind::Combobox
24
32
  include Tailwind::Avatar
25
33
  include Tailwind::Button
@@ -28,10 +36,14 @@ module StimulusPlumbers
28
36
  include Tailwind::Card
29
37
  include Tailwind::Form
30
38
  include Tailwind::Form::Field
39
+ include Tailwind::Form::Code
40
+ include Tailwind::Form::CreditCard
31
41
  include Tailwind::Form::Input
32
42
  include Tailwind::Icon
43
+ include Tailwind::Indicator
33
44
  include Tailwind::Layout
34
45
  include Tailwind::Link
46
+ include Tailwind::Progress
35
47
  include Tailwind::Timeline
36
48
  include Tailwind::Timeline::Group
37
49
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stimulus_plumbers_tailwind
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.8
4
+ version: 0.4.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Chang
@@ -33,23 +33,30 @@ files:
33
33
  - CHANGELOG.md
34
34
  - LICENSE-DEPENDENCIES
35
35
  - README.md
36
+ - app/assets/stylesheets/stimulus_plumbers/tailwind/animations.css
36
37
  - docs/architecture.md
37
38
  - docs/guide.md
38
39
  - lib/generators/stimulus_plumbers/tailwind/install/install_generator.rb
39
40
  - lib/stimulus_plumbers/tailwind/engine.rb
41
+ - lib/stimulus_plumbers/tailwind/generators/animations_directive.rb
42
+ - lib/stimulus_plumbers/tailwind/generators/sources_directive.rb
40
43
  - lib/stimulus_plumbers/tailwind/version.rb
41
44
  - lib/stimulus_plumbers/themes/tailwind/avatar.rb
42
45
  - lib/stimulus_plumbers/themes/tailwind/button.rb
43
46
  - lib/stimulus_plumbers/themes/tailwind/button/group.rb
44
47
  - lib/stimulus_plumbers/themes/tailwind/calendar.rb
45
48
  - lib/stimulus_plumbers/themes/tailwind/card.rb
49
+ - lib/stimulus_plumbers/themes/tailwind/checklist.rb
46
50
  - lib/stimulus_plumbers/themes/tailwind/combobox.rb
47
51
  - lib/stimulus_plumbers/themes/tailwind/control.rb
48
52
  - lib/stimulus_plumbers/themes/tailwind/form.rb
53
+ - lib/stimulus_plumbers/themes/tailwind/form/code.rb
54
+ - lib/stimulus_plumbers/themes/tailwind/form/credit_card.rb
49
55
  - lib/stimulus_plumbers/themes/tailwind/form/field.rb
50
56
  - lib/stimulus_plumbers/themes/tailwind/form/input.rb
51
57
  - lib/stimulus_plumbers/themes/tailwind/icon.rb
52
58
  - lib/stimulus_plumbers/themes/tailwind/icons/custom.rb
59
+ - lib/stimulus_plumbers/themes/tailwind/icons/customs/progress-ring.svg
53
60
  - lib/stimulus_plumbers/themes/tailwind/icons/customs/spinner.svg
54
61
  - lib/stimulus_plumbers/themes/tailwind/icons/heroicon.rb
55
62
  - lib/stimulus_plumbers/themes/tailwind/icons/heroicons/outline/academic-cap.svg
@@ -700,9 +707,12 @@ files:
700
707
  - lib/stimulus_plumbers/themes/tailwind/icons/heroicons/solid/wrench.svg
701
708
  - lib/stimulus_plumbers/themes/tailwind/icons/heroicons/solid/x-circle.svg
702
709
  - lib/stimulus_plumbers/themes/tailwind/icons/heroicons/solid/x-mark.svg
710
+ - lib/stimulus_plumbers/themes/tailwind/indicator.rb
703
711
  - lib/stimulus_plumbers/themes/tailwind/layout.rb
704
712
  - lib/stimulus_plumbers/themes/tailwind/link.rb
705
713
  - lib/stimulus_plumbers/themes/tailwind/list.rb
714
+ - lib/stimulus_plumbers/themes/tailwind/ordered_list.rb
715
+ - lib/stimulus_plumbers/themes/tailwind/progress.rb
706
716
  - lib/stimulus_plumbers/themes/tailwind/timeline.rb
707
717
  - lib/stimulus_plumbers/themes/tailwind/timeline/group.rb
708
718
  - lib/stimulus_plumbers/themes/tailwind_theme.rb