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 +4 -4
- data/CHANGELOG.md +43 -1
- data/README.md +81 -63
- data/app/assets/tailwind/unmagic_components/engine.css +1164 -0
- data/lib/unmagic/components/action_view_helpers.rb +17 -1
- data/lib/unmagic/components/configuration.rb +8 -1
- data/lib/unmagic/components/control.rb +62 -0
- data/lib/unmagic/components/engine.rb +4 -5
- data/lib/unmagic/components/form_builder.rb +54 -6
- data/lib/unmagic/components/version.rb +1 -1
- data/lib/unmagic/components.rb +1 -0
- metadata +6 -4
- data/app/assets/stylesheets/unmagic/components.css +0 -1309
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c204e77700223b87a5c4a2d019bb285de3dd24f9a9c1530aa54dd4b798f171a7
|
|
4
|
+
data.tar.gz: 91b9db1448f9a74a259a565e7d8ac0d4db6cb75927f3680a0914517666134011
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
50
|
-
|
|
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
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
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
|
-
|
|
96
|
-
|
|
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
|
|
115
|
-
--color-
|
|
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
|
-
**
|
|
273
|
-
|
|
274
|
-
|
|
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
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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
|