its-swiss 0.1.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 +7 -0
- data/CHANGELOG.md +57 -0
- data/LICENSE +21 -0
- data/README.md +262 -0
- data/app/assets/javascripts/its_swiss/clipboard_controller.js +33 -0
- data/app/assets/stylesheets/its-swiss.css +16 -0
- data/app/assets/stylesheets/its_swiss/components.css +403 -0
- data/app/assets/stylesheets/its_swiss/grid.css +67 -0
- data/app/assets/stylesheets/its_swiss/reset.css +50 -0
- data/app/assets/stylesheets/its_swiss/specimen.css +37 -0
- data/app/assets/stylesheets/its_swiss/tokens.css +142 -0
- data/app/assets/stylesheets/its_swiss/transitions.css +47 -0
- data/app/assets/stylesheets/its_swiss/type.css +109 -0
- data/app/controllers/its_swiss/specimen_controller.rb +24 -0
- data/app/helpers/its_swiss/application_helper.rb +73 -0
- data/app/views/its_swiss/shared/_errors.html.erb +14 -0
- data/app/views/its_swiss/shared/_flash.html.erb +8 -0
- data/app/views/its_swiss/shared/_masthead.html.erb +15 -0
- data/app/views/its_swiss/shared/_pagination.html.erb +26 -0
- data/app/views/its_swiss/specimen/_buttons.html.erb +15 -0
- data/app/views/its_swiss/specimen/_footer.html.erb +7 -0
- data/app/views/its_swiss/specimen/_form.html.erb +18 -0
- data/app/views/its_swiss/specimen/_grid.html.erb +14 -0
- data/app/views/its_swiss/specimen/_masthead.html.erb +15 -0
- data/app/views/its_swiss/specimen/_messages.html.erb +13 -0
- data/app/views/its_swiss/specimen/_pagination.html.erb +4 -0
- data/app/views/its_swiss/specimen/_pairs.html.erb +8 -0
- data/app/views/its_swiss/specimen/_table.html.erb +15 -0
- data/app/views/its_swiss/specimen/_take.html.erb +19 -0
- data/app/views/its_swiss/specimen/_type.html.erb +24 -0
- data/app/views/its_swiss/specimen/_values.html.erb +18 -0
- data/app/views/its_swiss/specimen/show.html.erb +27 -0
- data/app/views/layouts/its_swiss/shell.html.erb +55 -0
- data/config/importmap.rb +5 -0
- data/config/routes.rb +6 -0
- data/lib/generators/its_swiss/install/install_generator.rb +70 -0
- data/lib/generators/its_swiss/install/templates/theme.css +44 -0
- data/lib/its-swiss.rb +3 -0
- data/lib/its_swiss/engine.rb +39 -0
- data/lib/its_swiss/form_builder.rb +140 -0
- data/lib/its_swiss/version.rb +3 -0
- data/lib/its_swiss.rb +46 -0
- metadata +172 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 844fa90d3877d55f3c22b850b00bedfe6156241216b1165aad929ad3540edbd8
|
|
4
|
+
data.tar.gz: 470159b689994419fb692d26c0d1ede1cd48c73508090a4cb9f472ffa95788cc
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 40e2d6de3e683bc1adffbaabefd3863e3886ce7462c4e071ed916ff45a743f08b1d590535b6c0732821fa56017299d182045c631da1a1e30a9f84e3c00b5d666
|
|
7
|
+
data.tar.gz: e3ef3c8e93c892330619ad27ac091a0e9a3e9f3b8613db69dda8ed5ddf0da0548e406f83912770a1cfd010612380481906060cc8163aebc85d682846d7b20e52
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Semver. Consumers pin `~> 0.1`.
|
|
4
|
+
|
|
5
|
+
## 0.1.0 — 2026-08-31
|
|
6
|
+
|
|
7
|
+
The first extraction, from Pandatone, which ships on it.
|
|
8
|
+
|
|
9
|
+
One consumer, not two, which is what the leading zero is for: the boundary was
|
|
10
|
+
drawn from one real application and the second has not been built yet. Expect
|
|
11
|
+
the surface to move — see "Unproven" below. Pin `~> 0.1` and read this file
|
|
12
|
+
before upgrading.
|
|
13
|
+
|
|
14
|
+
### Core
|
|
15
|
+
|
|
16
|
+
- Six-step OKLCH value scale, paper to ink, neutral as shipped and warmed by
|
|
17
|
+
two slots. An unset `--accent` falls back to ink.
|
|
18
|
+
- A reset, five type sizes on an alternating 1.33/1.5 ratio, and one vertical
|
|
19
|
+
ladder used for leading as much as for space.
|
|
20
|
+
- Grid primitives — `.page`, `.grid`, `.measure`, `.fields`, `.run`, `.stack` —
|
|
21
|
+
and never a grid. A child says its width with `--span`.
|
|
22
|
+
- Components: masthead and nav, footer, table, form and field, buttons,
|
|
23
|
+
definition list, pagination, errors and hints, copy, skip link.
|
|
24
|
+
- View transition names and durations, with `prefers-reduced-motion` honoured.
|
|
25
|
+
- Everything inside a cascade layer, so an application's own unlayered CSS
|
|
26
|
+
outranks it without out-specifying anything.
|
|
27
|
+
|
|
28
|
+
### Engine
|
|
29
|
+
|
|
30
|
+
- `its_swiss/shell` layout with `:title`, `:head`, `:mark`, `:nav`,
|
|
31
|
+
`:main_class` and `:footer` slots.
|
|
32
|
+
- Partials: masthead, flash, errors, pagination.
|
|
33
|
+
- `ItsSwiss::FormBuilder` — one field shape, with `for`, `aria-describedby`,
|
|
34
|
+
`aria-invalid` and the refused state wired every time.
|
|
35
|
+
- Helpers: `its_swiss_stylesheet_tags`, `nav_link_to`, `copy_button`,
|
|
36
|
+
`its_swiss_form_with`, `its_swiss_page_numbers`.
|
|
37
|
+
- One Stimulus controller, pinned by the engine rather than by the installer.
|
|
38
|
+
- `its_swiss:install` generator: the theme file, the layout, the specimen route.
|
|
39
|
+
- `/its-swiss/specimen`, rendered twice — accent unset and accent set.
|
|
40
|
+
|
|
41
|
+
### Not in it, deliberately
|
|
42
|
+
|
|
43
|
+
- No typeface, no palette, no hue.
|
|
44
|
+
- No fixed grid, no `.span-N` classes, no utility classes.
|
|
45
|
+
- `.tag`, `.swatch` and the filter block stay in Pandatone until a second
|
|
46
|
+
application asks for them.
|
|
47
|
+
|
|
48
|
+
### Unproven
|
|
49
|
+
|
|
50
|
+
`.table` and `.pagination` have **no consumer at all**. Pandatone's migration
|
|
51
|
+
used neither, so the only thing that has ever rendered them is the specimen.
|
|
52
|
+
They are in the boundary the handoff drew and they break the library's own
|
|
53
|
+
rule about two applications; they are the two components most likely to move
|
|
54
|
+
when Stripeclub lands.
|
|
55
|
+
|
|
56
|
+
`.footer` is the same shape of guess — the shell has the slot and Pandatone
|
|
57
|
+
does not fill it.
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Bobby Meyer
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# its-swiss
|
|
2
|
+
|
|
3
|
+
A Swiss typographic style for Rails applications, in two layers.
|
|
4
|
+
|
|
5
|
+
The **core** is plain CSS and one small piece of JavaScript: tokens, a reset,
|
|
6
|
+
typography, grid primitives, components and view transition rules. It needs
|
|
7
|
+
nothing but a `<link>` tag.
|
|
8
|
+
|
|
9
|
+
The **engine** ships that core through `app/assets`, plus a base layout shell,
|
|
10
|
+
partials, a form builder, helpers and an install generator.
|
|
11
|
+
|
|
12
|
+
It is monochrome. A six-step value scale does the work and the one accent is
|
|
13
|
+
the consuming application's to set — it is not a theme with the colour left
|
|
14
|
+
out, it is a style whose argument is that the values are enough.
|
|
15
|
+
|
|
16
|
+
```ruby
|
|
17
|
+
gem "its-swiss"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
bin/rails generate its_swiss:install
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Then open `/its-swiss/specimen`.
|
|
25
|
+
|
|
26
|
+
## What is in the gem, and what stays in the application
|
|
27
|
+
|
|
28
|
+
| In the gem | Stays in the application |
|
|
29
|
+
| --- | --- |
|
|
30
|
+
| Tokens, reset, base typography | The grid itself — which blocks span which fields |
|
|
31
|
+
| Masthead, nav, footer, table, form, button, definition list, pagination | Domain components |
|
|
32
|
+
| A base layout shell with `content_for` slots | Page layouts beyond the shell |
|
|
33
|
+
| View transition names and durations | Which pages transition to which |
|
|
34
|
+
| The value scale and the accent slot | Any hue, any palette knowledge |
|
|
35
|
+
| A fallback grotesque stack | The typeface |
|
|
36
|
+
|
|
37
|
+
A pattern enters the gem after it appears in two applications, not before.
|
|
38
|
+
|
|
39
|
+
### The boundary is the cascade, not a convention
|
|
40
|
+
|
|
41
|
+
Everything the gem ships is inside a cascade layer. An application's own CSS is
|
|
42
|
+
unlayered, and an unlayered rule beats every layered one whatever its
|
|
43
|
+
specificity — so the application always wins, without having to out-specify
|
|
44
|
+
anything or reach for `!important`.
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
@layer its-swiss.tokens, its-swiss.reset, its-swiss.type,
|
|
48
|
+
its-swiss.grid, its-swiss.components, its-swiss.transitions;
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Each file declares its own layer, so linking the six files individually (what
|
|
52
|
+
`its_swiss_stylesheet_tags` does) and linking the single `its-swiss.css` that
|
|
53
|
+
imports them resolve identically.
|
|
54
|
+
|
|
55
|
+
## The slots an application fills
|
|
56
|
+
|
|
57
|
+
`bin/rails generate its_swiss:install` writes `app/assets/stylesheets/theme.css`
|
|
58
|
+
holding all of them. Nothing here has a default the gem could pick honestly.
|
|
59
|
+
|
|
60
|
+
| Slot | What it is |
|
|
61
|
+
| --- | --- |
|
|
62
|
+
| `--accent`, `--accent-ink` | State and emphasis only. Unset, the accent is ink |
|
|
63
|
+
| `--font-family` | The typeface. The gem ships none — declare `@font-face` and name it |
|
|
64
|
+
| `--value-chroma`, `--value-hue` | Warms the whole value scale together. Neutral as shipped |
|
|
65
|
+
| `--columns`, `--gutter`, `--baseline` | How many fields this problem has, and the unit everything vertical is measured in |
|
|
66
|
+
|
|
67
|
+
### The value scale
|
|
68
|
+
|
|
69
|
+
Six steps of OKLCH lightness, paper to ink. OKLCH because the steps have to be
|
|
70
|
+
perceptually even to read as a scale at all — the same arithmetic in sRGB puts
|
|
71
|
+
four of the six inside the top quarter of the range.
|
|
72
|
+
|
|
73
|
+
| Token | Lightness | Alias | For |
|
|
74
|
+
| --- | --- | --- | --- |
|
|
75
|
+
| `--value-0` | 98% | `--paper` | The ground |
|
|
76
|
+
| `--value-1` | 94% | `--paper-shaded` | A shaded ground |
|
|
77
|
+
| `--value-2` | 89% | `--rule` | A hairline |
|
|
78
|
+
| `--value-3` | 72% | `--rule-strong` | A rule that has to be seen |
|
|
79
|
+
| `--value-4` | 54% | `--ink-quiet` | Secondary text — 4.7:1 on paper |
|
|
80
|
+
| `--value-5` | 18% | `--ink` | Ink |
|
|
81
|
+
|
|
82
|
+
The ladder is deliberately not evenly spaced: a hairline has to be barely
|
|
83
|
+
there, so the top is dense; the middle only has to hold secondary text away
|
|
84
|
+
from primary, so it is sparse. An even ladder gives a usable mid-gray and no
|
|
85
|
+
usable rule.
|
|
86
|
+
|
|
87
|
+
Neutral as shipped. `--value-chroma: 0.006; --value-hue: 95` turns the whole
|
|
88
|
+
ladder to the warm grays of a printed page — and, near enough, to Pandatone's:
|
|
89
|
+
|
|
90
|
+
| Step | Warmed | Pandatone had |
|
|
91
|
+
| --- | --- | --- |
|
|
92
|
+
| `--value-0` | `#FAF8F4` | `#FAF9F7` `--ground` |
|
|
93
|
+
| `--value-2` | `#DCDBD6` | `#DCDAD4` `--keyline` |
|
|
94
|
+
| `--value-4` | `#706F6B` | `#6F6F6A` `--ink-quiet` |
|
|
95
|
+
| `--value-5` | `#12120F` | `#111111` `--ink` |
|
|
96
|
+
|
|
97
|
+
Within three units a channel, which is the cost of the four hand-picked
|
|
98
|
+
values becoming one ladder with a single chroma and a single hue. Measured in
|
|
99
|
+
Chromium, not calculated.
|
|
100
|
+
|
|
101
|
+
### The grid
|
|
102
|
+
|
|
103
|
+
The gem ships primitives and never a grid.
|
|
104
|
+
|
|
105
|
+
```css
|
|
106
|
+
.page /* the one measured container: --page-max wide, --page-inset either side */
|
|
107
|
+
.grid /* repeat(var(--columns), minmax(0, 1fr)) with --gutter between */
|
|
108
|
+
.measure /* three fields wide */
|
|
109
|
+
.fields /* --span fields wide */
|
|
110
|
+
.run /* items on a shared baseline, a space apart, wrapping when they must */
|
|
111
|
+
.stack /* the same, turned ninety degrees */
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
A child of `.grid` says how many fields it takes with `--span`; a child that
|
|
115
|
+
says nothing runs the whole field, because that is what nearly everything on a
|
|
116
|
+
page does.
|
|
117
|
+
|
|
118
|
+
`--field` is derived from the page rather than measured, so anything built on
|
|
119
|
+
it stops on a field line:
|
|
120
|
+
|
|
121
|
+
```css
|
|
122
|
+
--field: calc((var(--page-max) - var(--page-inset) * 2
|
|
123
|
+
- var(--gutter) * (var(--columns) - 1)) / var(--columns));
|
|
124
|
+
--measure: calc(var(--field) * 3 + var(--gutter) * 2);
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## The layout shell
|
|
128
|
+
|
|
129
|
+
```ruby
|
|
130
|
+
class ApplicationController < ActionController::Base
|
|
131
|
+
layout "its_swiss/shell"
|
|
132
|
+
end
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Slots, all optional:
|
|
136
|
+
|
|
137
|
+
| Slot | |
|
|
138
|
+
| --- | --- |
|
|
139
|
+
| `:title` | Falls back to the application's name |
|
|
140
|
+
| `:head` | Anything else that belongs in `<head>` |
|
|
141
|
+
| `:mark` | The wordmark. No mark and no nav means no masthead at all |
|
|
142
|
+
| `:nav` | The destinations |
|
|
143
|
+
| `:main_class` | What the page's main region is, if it is a grid |
|
|
144
|
+
| `:footer` | Whatever belongs after the page |
|
|
145
|
+
|
|
146
|
+
The shell writes the view transition opt-in, the six stylesheet links, the
|
|
147
|
+
importmap tags, a skip link, and the flash. It stops there — a page layout
|
|
148
|
+
beyond the shell is the application's, for the same reason its grid is.
|
|
149
|
+
|
|
150
|
+
## Helpers
|
|
151
|
+
|
|
152
|
+
| | |
|
|
153
|
+
| --- | --- |
|
|
154
|
+
| `its_swiss_stylesheet_tags` | The six links, tracked for Turbo |
|
|
155
|
+
| `nav_link_to(name, url, current:)` | A destination, with `aria-current` when you are at it |
|
|
156
|
+
| `copy_button(value)` | A value that copies itself |
|
|
157
|
+
| `its_swiss_form_with(...)` | `form_with`, already holding the library's builder |
|
|
158
|
+
| `its_swiss_page_numbers(page, pages)` | Which numbers a run of them shows, elided |
|
|
159
|
+
|
|
160
|
+
## The form builder
|
|
161
|
+
|
|
162
|
+
One shape for every field: a label, a control, and — when there is something to
|
|
163
|
+
say — a hint or the reason it was refused.
|
|
164
|
+
|
|
165
|
+
```erb
|
|
166
|
+
<%= its_swiss_form_with model: @palette do |form| %>
|
|
167
|
+
<%= form.text_field :name, hint: "As it appears in the nav." %>
|
|
168
|
+
<%= form.check_box :published %>
|
|
169
|
+
<%= form.submit "Save" %>
|
|
170
|
+
<% end %>
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
It exists because the parts that get left out by hand are the parts nobody
|
|
174
|
+
sees missing: a label whose `for` does not match its input does not enlarge the
|
|
175
|
+
target; a hint beside a control is a hint a screen reader never reaches; a
|
|
176
|
+
refused field coloured by CSS alone is a refusal only some readers get. The
|
|
177
|
+
builder wires `for`, `aria-describedby`, `aria-invalid` and `.field--invalid`
|
|
178
|
+
every time.
|
|
179
|
+
|
|
180
|
+
`label: false` hides the label rather than removing it — the name moves onto
|
|
181
|
+
the control as `aria-label`.
|
|
182
|
+
|
|
183
|
+
## The specimen
|
|
184
|
+
|
|
185
|
+
`/its-swiss/specimen`, mounted by the installer under `if Rails.env.development?`
|
|
186
|
+
and refused a second time by the controller, because a route is a line in a file
|
|
187
|
+
someone can move.
|
|
188
|
+
|
|
189
|
+
It renders every component, the type scale, the value scale and the grid
|
|
190
|
+
primitives — **twice**. The first take is the library exactly as it ships, with
|
|
191
|
+
the accent collapsed onto ink; the second sets an accent and changes nothing
|
|
192
|
+
else. If the first reads correctly, the value scale is doing the work.
|
|
193
|
+
|
|
194
|
+
It is the documentation and the regression fixture.
|
|
195
|
+
|
|
196
|
+
## Tests
|
|
197
|
+
|
|
198
|
+
```sh
|
|
199
|
+
bin/test # everything; browser tests skip
|
|
200
|
+
CHROME_BINARY=... CHROMEDRIVER=... bin/test # including the browser tests
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Tests come first, and a guard is only kept if removing what it guards makes it
|
|
204
|
+
fail.
|
|
205
|
+
|
|
206
|
+
Most of the suite reads the CSS rather than rendering it, which is enough for
|
|
207
|
+
the decisions that are decisions about the source: that the value scale is a
|
|
208
|
+
descending ladder, that no stylesheet invents a colour or a column count, that
|
|
209
|
+
every line box is measured in baselines, that no signal rests on colour alone.
|
|
210
|
+
|
|
211
|
+
The rest needs a browser, because a rule on the wrong selector reads correctly
|
|
212
|
+
in the CSS and does nothing on a page. Those assert what Chromium actually
|
|
213
|
+
resolved: that an unlayered declaration beats the library's layered one, that
|
|
214
|
+
the accent unset is ink, that quiet ink clears 4.5:1, that every line box is a
|
|
215
|
+
whole number of baselines, that nothing escapes the page at 390px, and that the
|
|
216
|
+
measure lands on a field line the browser really laid out. They skip loudly
|
|
217
|
+
rather than pretending to have checked.
|
|
218
|
+
|
|
219
|
+
There are no pixel tests.
|
|
220
|
+
|
|
221
|
+
## Coming from Pandatone
|
|
222
|
+
|
|
223
|
+
The token names changed where the boundary moved. The value scale is the same
|
|
224
|
+
ladder; the names are the library's rather than the application's.
|
|
225
|
+
|
|
226
|
+
| Pandatone | its-swiss |
|
|
227
|
+
| --- | --- |
|
|
228
|
+
| `--ground` | `--paper` |
|
|
229
|
+
| `--keyline` | `--rule` |
|
|
230
|
+
| `--ink-quiet`, `--ink` | unchanged |
|
|
231
|
+
| `--accent` | unchanged — still the application's to set |
|
|
232
|
+
| `--font` | `--font-family` |
|
|
233
|
+
| `--size-1..5`, `--space-N`, `--baseline`, `--measure`, `--page-max` | unchanged |
|
|
234
|
+
| `.masthead__nav` | `.nav` |
|
|
235
|
+
| `.channels` | `.pairs` |
|
|
236
|
+
| `.form`, `.field`, `.button*`, `.copy`, `.errors`, `.hint`, `.empty` | unchanged |
|
|
237
|
+
| `--columns-dense`, `--card`, `--card-wide`, `.swatch*`, `.tag*`, `.filter*` | stay in Pandatone |
|
|
238
|
+
|
|
239
|
+
Two behavioural differences to know about:
|
|
240
|
+
|
|
241
|
+
- **The masthead no longer sits on the page's field.** It lays itself out in
|
|
242
|
+
flex, so it does not break when an application changes `--columns`.
|
|
243
|
+
- **`--field` now subtracts the page's own inset** before dividing. Pandatone's
|
|
244
|
+
`--measure` was a gutter and a half wider than three fields; anything relying
|
|
245
|
+
on the old number moves in by 24px.
|
|
246
|
+
- **`.button--danger` is set apart by a step on the ladder, not an auto
|
|
247
|
+
margin.** Pandatone scoped the auto margin to two named regions; a library
|
|
248
|
+
cannot, and an auto margin across a whole page reads as a different control
|
|
249
|
+
rather than a separated one.
|
|
250
|
+
|
|
251
|
+
## Versioning
|
|
252
|
+
|
|
253
|
+
Semver with a changelog. Consumers pin `~> 0.1`.
|
|
254
|
+
|
|
255
|
+
`0.1.0` ships with one consumer — Pandatone — and that is what the leading
|
|
256
|
+
zero is for: the boundary was drawn from one real application and the second
|
|
257
|
+
has not been built yet. The surface will move. Read `CHANGELOG.md` before
|
|
258
|
+
upgrading, and see `RELEASING.md` for how a version gets out.
|
|
259
|
+
|
|
260
|
+
## License
|
|
261
|
+
|
|
262
|
+
MIT.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
// The library's whole JavaScript surface. A typographic system does not need
|
|
4
|
+
// script to set type; this is here because a value that exists to be taken
|
|
5
|
+
// somewhere else should be a button that copies itself, and that cannot be
|
|
6
|
+
// done in CSS.
|
|
7
|
+
//
|
|
8
|
+
// The value is an attribute rather than the button's own text, so a button
|
|
9
|
+
// that shows a value shortened for the page still copies the whole of it.
|
|
10
|
+
export default class extends Controller {
|
|
11
|
+
static values = { text: String, saidFor: { type: Number, default: 1200 } }
|
|
12
|
+
|
|
13
|
+
async copy() {
|
|
14
|
+
try {
|
|
15
|
+
await navigator.clipboard.writeText(this.textValue || this.element.textContent.trim())
|
|
16
|
+
} catch {
|
|
17
|
+
// A refused or unavailable clipboard is not an error the page should
|
|
18
|
+
// report: the value is still on screen, still selectable, and saying
|
|
19
|
+
// "copied" when nothing was copied is the only outcome worth avoiding.
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Says it worked in the one place you are already looking. The attribute
|
|
24
|
+
// is the whole signal; what it looks like is components.css's business.
|
|
25
|
+
this.element.setAttribute("data-copied", "")
|
|
26
|
+
clearTimeout(this.timeout)
|
|
27
|
+
this.timeout = setTimeout(() => this.element.removeAttribute("data-copied"), this.saidForValue)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
disconnect() {
|
|
31
|
+
clearTimeout(this.timeout)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/* The whole library in one <link>.
|
|
2
|
+
*
|
|
3
|
+
* The layer order is stated before the first import rather than left to the
|
|
4
|
+
* order the files arrive in, so a slow network cannot reorder the cascade.
|
|
5
|
+
* Every file states its own layer as well, which is what makes this file and
|
|
6
|
+
* six separate <link> tags resolve identically — the Rails helper writes the
|
|
7
|
+
* six, and anything else can point at this.
|
|
8
|
+
*/
|
|
9
|
+
@layer its-swiss.tokens, its-swiss.reset, its-swiss.type, its-swiss.grid, its-swiss.components, its-swiss.transitions;
|
|
10
|
+
|
|
11
|
+
@import url("its_swiss/tokens.css");
|
|
12
|
+
@import url("its_swiss/reset.css");
|
|
13
|
+
@import url("its_swiss/type.css");
|
|
14
|
+
@import url("its_swiss/grid.css");
|
|
15
|
+
@import url("its_swiss/components.css");
|
|
16
|
+
@import url("its_swiss/transitions.css");
|