unmagic-components 0.2.0 → 0.3.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: 73a26932047bf5f5019925414115576c98d199f51e434ebae9e614423ba67022
4
- data.tar.gz: 93be93b5f8b3c9c42c8224a0ec229635a270a89f5319477e0b99970cb4ab4e8c
3
+ metadata.gz: c204e77700223b87a5c4a2d019bb285de3dd24f9a9c1530aa54dd4b798f171a7
4
+ data.tar.gz: 91b9db1448f9a74a259a565e7d8ac0d4db6cb75927f3680a0914517666134011
5
5
  SHA512:
6
- metadata.gz: 1269dce8cd8dac1c5d3d8e9f0ac2437bf4b2bd25e2511d5dc8aee4010157dfb9156be984fc2976029995dc704d3bb4ac8b880ea2683f51571f9bc88edf3cac8d
7
- data.tar.gz: 4a6e77ce020686fecbbe0c09247ab61769128b263ff47cea7df9549b3b360670e068400743bfae9a69d467250479f0768e57d57581d651927c686800ccefd868
6
+ metadata.gz: af2099fb9686c1b87742f79a07ee892a7dc3b8c984f93b684eb3a51355c82b793369230857ff9f8e5eff2ee24e7fda85d482f215126eb6ba7df59a3c62cd7aa9
7
+ data.tar.gz: a975d65b9b5575e6cbb2768dd22232f8c20291eecfcb7984fc075b6d6c2acefd1e56f558d3ca96a92329ae24588cc35042d780ecd1a2558d2fc7abe8e3aa3323
data/CHANGELOG.md CHANGED
@@ -7,6 +7,47 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.3.0] - 2026-09-16
11
+
12
+ ### Added
13
+
14
+ - Styled form controls:
15
+ - `FormBuilder`'s text-like fields (`text_field`, `email_field`,
16
+ `password_field`, `date_field`, `text_area` and the rest) wear
17
+ `UnmagicInput`.
18
+ - Its selects wear `UnmagicSelect`.
19
+ - `check_box_field` and `check_box_collection` wear `UnmagicCheck`.
20
+ - The classes come from the new `config.control_class` seam, called with
21
+ `(view, kind)`.
22
+ - `control_classes(kind, size:)` gives a control outside the builder the same
23
+ classes, for a `select_tag` or a hand-written radio. It takes `:small` and
24
+ `:large` sizes that match `button_classes`.
25
+ - The preview app (`bin/dev`) is rebuilt as component docs: a sidebar, an
26
+ overview of every component with a live thumbnail, and a page per component
27
+ whose examples each show their source beside the rendered result.
28
+
29
+ ### Changed
30
+
31
+ - **Breaking: the styles are Tailwind CSS v4, and your app needs Tailwind v4.**
32
+ - The gem's styles are now a Tailwind source file,
33
+ `app/assets/tailwind/unmagic_components/engine.css`, compiled by your app's
34
+ own Tailwind build.
35
+ - It uses Tailwind's palette with `dark:` variants, so dark mode follows your
36
+ app's `dark` variant.
37
+ - The rules sit in `@layer components`, so utilities passed as `class:`
38
+ override them.
39
+ - `unmagic/components.css` and every `--unmagic-*` theming variable are
40
+ removed.
41
+ - To migrate: drop `stylesheet_link_tag "unmagic/components"`, and add
42
+ `@import "../builds/tailwind/unmagic_components";` after
43
+ `@import "tailwindcss";` in `app/assets/tailwind/application.css`. Then move
44
+ any `--unmagic-*` overrides into your `@theme` colours or your `dark`
45
+ variant.
46
+ - **Form controls now carry a class, and the gem's CSS styles them.** An app
47
+ that styles its inputs itself keeps its own look with
48
+ `config.control_class = ->(_view, _kind) { nil }`, or points the seam at its
49
+ own classes.
50
+
10
51
  ## [0.2.0] - 2026-09-16
11
52
 
12
53
  ### Added
@@ -120,6 +161,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
120
161
  - Configurable empty-state, pagination and Pagy seams so the gem depends on neither
121
162
  Pagy nor any host helper.
122
163
 
123
- [Unreleased]: https://github.com/unreasonable-magic/unmagic-components/compare/v0.2.0...HEAD
164
+ [Unreleased]: https://github.com/unreasonable-magic/unmagic-components/compare/v0.3.0...HEAD
165
+ [0.3.0]: https://github.com/unreasonable-magic/unmagic-components/compare/v0.2.0...v0.3.0
124
166
  [0.2.0]: https://github.com/unreasonable-magic/unmagic-components/compare/v0.1.0...v0.2.0
125
167
  [0.1.0]: https://github.com/unreasonable-magic/unmagic-components/releases/tag/v0.1.0
data/README.md CHANGED
@@ -44,10 +44,29 @@ the spirit of `form_for`: describe the columns, get the chrome.
44
44
  gem "unmagic-components"
45
45
  ```
46
46
 
47
- Add the stylesheet to your layout:
47
+ The components are styled with **Tailwind CSS v4, which your app needs**. The
48
+ gem's styles are a Tailwind source file,
49
+ `app/assets/tailwind/unmagic_components/engine.css`, that your own Tailwind
50
+ build compiles. Markup inside an installed gem is never scanned, so the gem
51
+ hands Tailwind its CSS instead of relying on scanning.
48
52
 
49
- ```erb
50
- <%= stylesheet_link_tag "unmagic/components" %>
53
+ With tailwindcss-rails, import the engine's entry file after Tailwind itself in
54
+ `app/assets/tailwind/application.css`:
55
+
56
+ ```css
57
+ @import "tailwindcss";
58
+ @import "../builds/tailwind/unmagic_components";
59
+ ```
60
+
61
+ tailwindcss-rails generates that entry file on every build and watch, or on
62
+ demand with `bin/rails tailwindcss:engines`.
63
+
64
+ With the Tailwind CLI or an npm build, import the gem's file by path. `bundle
65
+ show unmagic-components` prints where the gem is installed:
66
+
67
+ ```css
68
+ @import "tailwindcss";
69
+ @import "/path/to/unmagic-components/app/assets/tailwind/unmagic_components/engine.css";
51
70
  ```
52
71
 
53
72
  The engine mixes the helpers into ActionView automatically — no initializer
@@ -57,65 +76,38 @@ to your application.js (see [Dialogs](#dialogs)).
57
76
 
58
77
  ## Theming
59
78
 
60
- The stylesheet is plain CSS driven by custom properties, and every value falls
61
- back to a Tailwind palette default, so the components look right unconfigured.
62
- To match your own design, set the `--unmagic-*` knobs wherever your theme lives:
79
+ The components use Tailwind's own palette (neutral for surfaces, borders and
80
+ text, and red, green and amber for tones), with a `dark:` variant for every
81
+ colour. They look right with Tailwind's defaults and follow your theme from
82
+ there.
83
+
84
+ **Dark mode** follows your app's `dark` variant. Tailwind's default is the
85
+ visitor's system setting. To switch on a class or an attribute instead, redefine
86
+ the variant in your Tailwind input file and the components switch with it:
63
87
 
64
88
  ```css
65
- :root {
66
- --unmagic-surface: var(--surface);
67
- --unmagic-surface-2: var(--surface-2);
68
- --unmagic-surface-3: var(--surface-3); /* neutral badges, tab tracks */
69
- --unmagic-raised: var(--surface); /* the selected tab */
70
- --unmagic-hover: var(--hover);
71
- --unmagic-border: var(--border);
72
- --unmagic-border-strong: var(--border-strong);
73
- --unmagic-text: var(--text);
74
- --unmagic-text-2: var(--text-2);
75
- --unmagic-text-3: var(--text-3);
76
- --unmagic-skeleton: var(--surface-3);
77
- --unmagic-accent: var(--primary); /* primary buttons */
78
- --unmagic-on-accent: var(--on-primary);
79
- --unmagic-bad: var(--danger); /* errors, danger buttons */
80
- --unmagic-bad-surface: var(--danger-surface);
81
- --unmagic-bad-border: var(--danger-border);
82
- --unmagic-good: var(--success); /* good toasts */
83
- --unmagic-good-surface: var(--success-surface);
84
- --unmagic-good-border: var(--success-border);
85
- --unmagic-warn: var(--warning); /* warn toasts */
86
- --unmagic-warn-surface: var(--warning-surface);
87
- --unmagic-warn-border: var(--warning-border);
88
- --unmagic-tooltip: var(--inverse); /* tooltips */
89
- --unmagic-on-tooltip: var(--on-inverse);
90
- --unmagic-focus: var(--focus-ring);
91
- --unmagic-backdrop: rgb(0 0 0 / 0.5); /* behind dialogs */
92
- }
89
+ @custom-variant dark (&:where(.dark, .dark *));
93
90
  ```
94
91
 
95
- In a dark theme, set `--unmagic-accent` too. Its fallback is near-black, which
96
- disappears against a dark dialog.
97
-
98
- Dark mode needs nothing extra. If your own tokens already flip, these flip with
99
- them — the gem ships no dark variant and makes no assumption about how you
100
- select a theme.
101
-
102
- The CSS is deliberately **not** part of any Tailwind build. Tailwind only
103
- generates classes it can see and it does not scan installed gems, so a component
104
- library that emitted utilities from Ruby would render unstyled in your app
105
- unless you pointed `@source` at the gem's install path.
106
-
107
- If you do want Tailwind utilities from these tokens, map them with `@theme
108
- inline` — not plain `@theme`. A non-inline theme variable makes the utility emit
109
- `var(--color-unmagic-surface)`, which resolves *where that variable is defined*,
110
- so an override scoped to a subtree (a themed preview pane, a `.dark` region)
111
- would be invisible to it:
92
+ **Colours, radii and fonts** come from your theme, so change them there and the
93
+ components follow:
112
94
 
113
95
  ```css
114
- @theme inline {
115
- --color-unmagic-surface: var(--unmagic-surface);
96
+ @theme {
97
+ --color-neutral-900: oklch(0.21 0.03 265);
98
+ --radius-md: 0.25rem;
116
99
  }
117
100
  ```
118
101
 
102
+ **One component** takes utilities through `class:`, like any other option.
103
+ The gem's rules sit in `@layer components`, which Tailwind orders before
104
+ `@layer utilities`, so your utilities win:
105
+
106
+ ```erb
107
+ <%= card title: "Members", class: "rounded-none shadow-none" do %>…<% end %>
108
+ <%= form.field :email, "Email", class: "font-mono" %>
109
+ ```
110
+
119
111
  ## Configuration
120
112
 
121
113
  Three seams, each with a working default. Point them at your own versions if you
@@ -269,19 +261,45 @@ Set it as the default with `config.action_view.default_form_builder`, or pass
269
261
  Outside a form builder, use `uuid_input_tag`. Needs
270
262
  `import "unmagic/components/uuid_input"`.
271
263
 
272
- **What the control looks like is not decided here.** Apps style inputs in
273
- incompatible ways a class on every input, or a bare-element rule and a
274
- component library that picked one would be wrong in the other. So the builder emits
275
- structure and the gem's CSS styles only the label, hint and error. Point your own
276
- input rules at `.UnmagicField` to give its controls your look:
264
+ **The controls are styled too.** Each control the builder makes wears a class for
265
+ its kind, and the gem's CSS styles those classes, never bare elements, so an input
266
+ the gem didn't render keeps whatever your app gives it:
277
267
 
278
- ```css
279
- .field,
280
- .UnmagicField {
281
- /* your existing input rules */
282
- }
268
+ | Builder methods | Kind | Class |
269
+ |---|---|---|
270
+ | `text_field`, `email_field`, `number_field`, `url_field`, `search_field`, `telephone_field` | `:input` | `UnmagicInput` |
271
+ | `password_field` | `:password` | `UnmagicInput` |
272
+ | `text_area`, `autogrow_text_area` | `:text_area` | `UnmagicInput` |
273
+ | `date_field`, `time_field`, `datetime_field`, `month_field`, `week_field` | `:date` | `UnmagicInput` |
274
+ | `select`, `collection_select`, `grouped_collection_select`, `time_zone_select` | `:select` | `UnmagicSelect` |
275
+ | `check_box_field`, `check_box_collection` | `:check` | `UnmagicCheck` |
276
+
277
+ A class you pass (`class: "font-mono"`) is added after the gem's. Rails' own
278
+ `check_box` and `radio_button` are left alone, as are the `*_tag` helpers. Give
279
+ one of those the same look with `control_classes`:
280
+
281
+ ```erb
282
+ <%= select_tag "status", options_for_select(%w[Open Closed]), class: control_classes(:select) %>
283
+ <%= search_field_tag "q", params[:q], class: control_classes(:input, size: :small) %>
284
+ <%= radio_button_tag "notify", "daily", class: control_classes(:radio) %>
283
285
  ```
284
286
 
287
+ `control_classes(kind, size:)` takes the kinds above plus `:radio`. `size:
288
+ :small` or `:large` sits a box level with a `button_classes` button of the same
289
+ size.
290
+
291
+ The classes come from a seam. If your app already styles its inputs, point it at
292
+ your own classes, or return `nil` to leave the controls unstyled:
293
+
294
+ ```ruby
295
+ config.control_class = ->(_view, kind) { kind == :select ? "form-select" : "form-input" }
296
+ config.control_class = ->(_view, _kind) { nil }
297
+ ```
298
+
299
+ The controls use the same palette as the rest of the gem. A checked box or radio
300
+ is neutral-900 (neutral-100 in dark mode), and an invalid control has a red
301
+ border.
302
+
285
303
  The submit button's classes come from a seam, so it wears your own button:
286
304
 
287
305
  ```ruby