its-swiss 0.8.0 → 0.9.1

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: 609e2de8676ac502b9fd59c7c0eb8fa4a57e2c6ba5a0a7ab37b618f81b3203b4
4
- data.tar.gz: 7fced9860618baa6456eb521f4b2a1e6fecb667f8adc3c9f550c990d067669c7
3
+ metadata.gz: 7ab6df97c9cfca52b616b05f5e985aa37df4253563fd1b1177c9c67890706cad
4
+ data.tar.gz: 6e9d6316b70b2670b7b307b33ec64955e88d562d085c95a86e07836e635d1a97
5
5
  SHA512:
6
- metadata.gz: 0fc7073ab2bf1bc8f39db1a2124b08e746d3811f71fbf38e0573af7fb6e59912e776e3f6bddce62802b08d9ae6528f5eb51d15d147904e0d4516f69db738df9c
7
- data.tar.gz: 914e4ea3dee558b14821e3a0b3ce134220520b7be6d9d5d10727d2f8f6b173d025868aa30c4316b81e523ff7bfb8fb5a2563a2219a33b2686f9be2b8b0753fca
6
+ metadata.gz: 5a3f8671a25c996f2883add93ead145a89244a8a5bbec670022c0b3d49d587d19dd1b5a664eb94bd49908b1d8a4b28877e66b180ac49d3b653b47b0493cf7d02
7
+ data.tar.gz: 286de25b2203d9928f9a4cbf596341aa8d450c3cfaecf016cafff44737586df4d4cd65d98271d102f2dcf2af9e8f9e058413ead233ad86602a3e242a5f471a4d
data/CHANGELOG.md CHANGED
@@ -2,6 +2,52 @@
2
2
 
3
3
  Semver. Consumers pin `~> 0.1`.
4
4
 
5
+ ## 0.9.1 — 2026-09-07
6
+
7
+ ### Fixed
8
+
9
+ - **The suite, and so the release, under json 3.0.0.** json 3.0.0 came out
10
+ twenty minutes after 0.9.0's pull request went green and changed the
11
+ signature of `JSON.parse`; Active Support 8.1.3.1 still calls it the old
12
+ way when it reads a signed cookie, so the session and the flash raised on
13
+ every request and the release of 0.9.0 stopped at its own tests. The
14
+ Gemfile holds json below 3 until a Rails that takes it. Nothing in the
15
+ library changed; 0.9.1 is 0.9.0 released.
16
+
17
+ ## 0.9.0 — 2026-09-07
18
+
19
+ A day of use, and what a day asks of a style: less talk and one red.
20
+
21
+ ### Added
22
+
23
+ - **Sections.** `page_head(title, sections:)` names the surfaces of one
24
+ page under its title — what a thing is made of, what it wears, how it
25
+ leaves — the one shown in the weight, and a page shows that one alone.
26
+ A working page must not scroll: what is done daily is reached from the
27
+ head, in the same position on every page, never by scrolling past what
28
+ was done yesterday. `page_sections(sections)` writes the nav on its own.
29
+
30
+ - **Explanations.** `explain(text)` puts what a section means behind one
31
+ mark: a question mark on the line, closed, and the text in the hint
32
+ register under it when it is asked for. A sentence over every table is
33
+ needed on the first day and never again.
34
+
35
+ ### Changed
36
+
37
+ - **One red per page.** The chosen filter and a current menu are in the
38
+ weight, in ink. The accent is for where you are on the site — the nav,
39
+ the subnav, the page numbers — and for the one thing that cannot be
40
+ undone. A page that said "this one" in red five times above the fold
41
+ had no signal left.
42
+
43
+ - **Hints are prose.** `.hint` is set at body size. The small size is for
44
+ labels and metadata, and a hint set in it at a body line's leading read
45
+ as a list of afterthoughts. Table headings go the other way: the label
46
+ register, bold, on the line.
47
+
48
+ - **The filter block is ruled once, below.** The rule above it was a third
49
+ rule in ten lines, and the rule is the one mark this style draws.
50
+
5
51
  ## 0.8.0 — 2026-09-07
6
52
 
7
53
  The third consumer, and what it and the second found in common.
data/README.md CHANGED
@@ -329,12 +329,36 @@ a run beside them, wrapping under the title when the page is narrow.
329
329
 
330
330
  The document is titled from it unless the view has already set `:title`.
331
331
 
332
+ A page with several surfaces — what a thing is made of, what it wears, how it
333
+ leaves — names them under the title with `sections:`, each a name, a URL and
334
+ whether it is the one shown, and shows that one alone. A working page must
335
+ not scroll: what is done daily is reached from the head, in the same position
336
+ on every page.
337
+
338
+ ```erb
339
+ <%= page_head @pattern.name, sections: [ [ "Compose", pattern_path(@pattern), @section == "compose" ],
340
+ [ "Dress", pattern_path(@pattern, section: "dress"), @section == "dress" ] ] do %>
341
+ ```
342
+
343
+ ### Explanations
344
+
345
+ What a section means, behind one mark. A sentence over every table is needed
346
+ on the first day and never again, and a tool used daily is read on every
347
+ other day; so `explain` writes it in the hint register, closed, and a
348
+ question mark on the line opens it.
349
+
350
+ ```erb
351
+ <%= explain "The repeat, in order along the stripe normal." %>
352
+ ```
353
+
332
354
  ### Filters and cards
333
355
 
334
356
  What narrows a list, and the list. A search that filters as you type, and a
335
357
  register for each other way of narrowing it — a tag, an order, a size — in the
336
358
  same two positions every time: a quiet label, then the choices, the one in
337
- force carrying `aria-current`, which the CSS colours and weights.
359
+ force carrying `aria-current`, which the CSS sets in the weight. In ink, not
360
+ the accent: the accent is for where you are on the site and for the one
361
+ thing that cannot be undone, and a page has one red.
338
362
 
339
363
  ```erb
340
364
  <div class="filters">
@@ -419,7 +443,9 @@ beyond the shell is the application's, for the same reason its grid is.
419
443
  | `copy_button(value)` | A value that copies itself |
420
444
  | `its_swiss_form_with(...)` | `form_with`, already holding the library's builder |
421
445
  | `its_swiss_page_numbers(page, pages)` | Which numbers a run of them shows, elided |
422
- | `page_head(title, lede:) { actions }` | The one shape every page opens with |
446
+ | `page_head(title, lede:, sections:) { actions }` | The one shape every page opens with |
447
+ | `page_sections(sections)` | The surfaces of one page, the one shown in the weight |
448
+ | `explain(text) { }` | What a section means, behind one mark |
423
449
  | `filter_register(label, choices, name:)` | One register of a filter block |
424
450
  | `search_form(url, frame:, keep:)` | A search that narrows a list as you type |
425
451
 
@@ -95,10 +95,11 @@
95
95
  .menu > summary:hover,
96
96
  .menu[open] > summary { color: var(--ink); }
97
97
 
98
- /* Where you are, when where you are is inside the menu: said twice, the
99
- way the nav says it. */
98
+ /* Where you are, when where you are is inside the menu: the weight, in
99
+ ink. Not the accent a page has one red, and the subnav under a menu
100
+ that is current already names the place in it. */
100
101
  .menu--current > summary {
101
- color: var(--accent);
102
+ color: var(--ink);
102
103
  font-weight: 700;
103
104
  }
104
105
 
@@ -206,6 +207,62 @@
206
207
  than holding their one-line width and pushing a phone's page sideways. */
207
208
  .page-head__actions { flex: 0 1 auto; }
208
209
 
210
+ /* --- Sections -----------------------------------------------------------
211
+ One page, several surfaces: what a thing is made of, what it wears,
212
+ how it leaves. A run of names under the title, the one you are on in
213
+ the weight, and the surface below it the only thing on the page. A
214
+ working page must not scroll: what is done every day is reached from
215
+ the head, in the same position on every page, and never by scrolling
216
+ past what was done yesterday. */
217
+ .sections {
218
+ display: flex;
219
+ flex-wrap: wrap;
220
+ gap: var(--line) var(--space-3);
221
+ }
222
+
223
+ /* Its own row in the head, under the title and the actions. */
224
+ .page-head > .sections { flex-basis: 100%; }
225
+
226
+ .sections a {
227
+ display: inline-block;
228
+ color: var(--ink-quiet);
229
+ text-decoration: none;
230
+ }
231
+
232
+ .sections a:hover { color: var(--ink); }
233
+
234
+ /* Where you are, in the weight. Ink, not the accent: the accent is for
235
+ where you are on the site, and this is where you are on the page. */
236
+ .sections [aria-current] {
237
+ color: var(--ink);
238
+ font-weight: 700;
239
+ }
240
+
241
+ /* --- Explanations -------------------------------------------------------
242
+ What a section means, behind one mark, for the day it is first read.
243
+ A sentence over every table is needed on the first day and never
244
+ again, and a tool used daily is read on every other day. So the
245
+ explanation is there, in the hint register, and closed: a question
246
+ mark on the line, and the text under it when it is asked for. A
247
+ details element, so it opens with no script. */
248
+ /* A block as wide as its one character, so the line it sits on is whole
249
+ and the target is the mark and not the row. */
250
+ .explain > summary {
251
+ list-style: none;
252
+ display: block;
253
+ width: max-content;
254
+ cursor: pointer;
255
+ color: var(--ink-quiet);
256
+ font-weight: 700;
257
+ }
258
+
259
+ .explain > summary::-webkit-details-marker { display: none; }
260
+
261
+ .explain > summary:hover,
262
+ .explain[open] > summary { color: var(--ink); }
263
+
264
+ .explain__body { margin-block-start: var(--line); }
265
+
209
266
  /* --- Rules --------------------------------------------------------------
210
267
  The one horizontal mark in the style, at the two weights tokens.css
211
268
  declares. */
@@ -301,9 +358,15 @@
301
358
  vertical-align: top;
302
359
  }
303
360
 
361
+ /* A heading is a label: the label register, bold, on the line. Body size
362
+ made it a second row of values. */
304
363
  .table th {
364
+ font-family: var(--face-200), var(--font-family);
365
+ font-size: var(--size-1);
366
+ line-height: var(--line);
305
367
  color: var(--ink-quiet);
306
368
  font-weight: 700;
369
+ letter-spacing: 0.01em;
307
370
  }
308
371
 
309
372
  .table thead th { border-bottom-color: var(--ink); }
@@ -613,17 +676,19 @@
613
676
  control: pick one of a handful. A register is a quiet label and then
614
677
  the choices, in the same two positions every time, so filtering and
615
678
  ordering read as the registers they are rather than as two rows of
616
- identical words. Ruled above and below: the block belongs to the list
617
- under it, not to the page head above. */
679
+ identical words. Ruled below, once: the block belongs to the list
680
+ under it, and the space above it is the page head's. A second rule
681
+ above it was a third rule in ten lines, and the rule is the one mark
682
+ this style draws, so it has to be rare. */
618
683
 
619
684
  .filters {
620
685
  display: flex;
621
686
  flex-wrap: wrap;
622
687
  align-items: flex-start;
623
688
  gap: var(--line) var(--space-3);
624
- /* The rules are drawn inside the box, so the block stays whole lines. */
625
- padding-block: calc(var(--line) - var(--rule-hair));
626
- border-block: var(--rule-hair) solid var(--rule);
689
+ /* The rule is drawn inside the box, so the block stays whole lines. */
690
+ padding-block-end: calc(var(--line) - var(--rule-hair));
691
+ border-block-end: var(--rule-hair) solid var(--rule);
627
692
  margin-block-end: var(--line);
628
693
  }
629
694
 
@@ -659,9 +724,11 @@
659
724
 
660
725
  .filter__choices a:hover { color: var(--ink); }
661
726
 
662
- /* Which one is chosen, said twice, for the reason the nav says it twice. */
727
+ /* Which one is chosen: the weight, in ink. The accent is for where you
728
+ are on the site and for the one thing that cannot be undone; a page
729
+ with four registers each saying "this one" in red has no signal left. */
663
730
  .filter__choices [aria-current] {
664
- color: var(--accent);
731
+ color: var(--ink);
665
732
  font-weight: 700;
666
733
  }
667
734
 
@@ -106,9 +106,12 @@
106
106
  .button--danger,
107
107
  .link-quiet { color: var(--ink-quiet); }
108
108
 
109
+ /* A hint is prose — a sentence about the thing beside it — and prose is
110
+ read at body size. The small size is for labels and metadata, and a
111
+ hint set in it read as a list of afterthoughts. */
109
112
  .hint {
110
- font-family: var(--face-200), var(--font-family);
111
- font-size: var(--size-1);
113
+ font-family: var(--face-150), var(--font-family);
114
+ font-size: var(--size-2);
112
115
  line-height: var(--line);
113
116
  max-width: var(--measure);
114
117
  }
@@ -232,8 +235,10 @@
232
235
  dd:not(:has(> :is(p, ul, ol, dl, div, figure, table, pre, blockquote))),
233
236
  .micro, .hint, .lede, .empty, .masthead__mark, .field__error, .errors__title,
234
237
  .filter__label, .card__name, .card__meta,
238
+ .explain > summary,
235
239
  /* Flex items are blockified, so the text in a row is trimmable too. */
236
240
  .nav a,
241
+ .sections a,
237
242
  .menu > summary,
238
243
  .menu__list a,
239
244
  .subnav a,
@@ -155,7 +155,13 @@ module ItsSwiss
155
155
  # <%= page_head "Palettes", lede: "Every palette in the library." do %>
156
156
  # <%= link_to "New palette", new_palette_path, class: "button button--primary" %>
157
157
  # <% end %>
158
- def page_head(title, lede: nil, &block)
158
+ #
159
+ # A page with several surfaces names them under the title, and the one
160
+ # you are on carries the weight:
161
+ #
162
+ # <%= page_head @pattern.name, sections: [ [ "Compose", pattern_path(@pattern), true ],
163
+ # [ "Dress", pattern_path(@pattern, section: "dress"), false ] ] do %>
164
+ def page_head(title, lede: nil, sections: nil, &block)
159
165
  content_for(:title, strip_tags(title.to_s)) unless content_for?(:title)
160
166
  actions = capture(&block) if block
161
167
 
@@ -164,11 +170,35 @@ module ItsSwiss
164
170
  tag.div(class: "page-head__title") do
165
171
  safe_join([ tag.h1(title, class: "page-title"), (tag.p(lede, class: "lede") if lede) ].compact)
166
172
  end,
167
- (tag.div(actions, class: "page-head__actions run") if actions.present?)
173
+ (tag.div(actions, class: "page-head__actions run") if actions.present?),
174
+ (page_sections(sections) if sections.present?)
168
175
  ].compact)
169
176
  end
170
177
  end
171
178
 
179
+ # The surfaces of one page: a name, a URL and whether it is the one
180
+ # shown, each a whole navigation so it survives a reload and can be
181
+ # sent to somebody. The page head places it; on its own it is a nav.
182
+ def page_sections(sections, label: "Sections")
183
+ tag.nav(class: "sections", aria: { label: label }) do
184
+ safe_join(sections.map { |text, url, current| link_to(text, url, aria: { current: ("page" if current) }) })
185
+ end
186
+ end
187
+
188
+ # What a section means, behind one mark. The explanation is written in
189
+ # the hint register and closed: a page used daily is read on every day
190
+ # but the first, and a sentence over every table is for the first.
191
+ #
192
+ # <%= explain "The repeat, in order along the stripe normal." %>
193
+ # <%= explain do %><p class="hint">…</p><% end %>
194
+ def explain(text = nil, label: "Explain", &block)
195
+ body = block ? capture(&block) : tag.p(text, class: "hint")
196
+
197
+ tag.details(class: "explain") do
198
+ safe_join([ tag.summary("?", aria: { label: label }), tag.div(body, class: "explain__body") ])
199
+ end
200
+ end
201
+
172
202
  # One register of a filter block: a quiet label, then the choices, each
173
203
  # a name, a URL and whether it is the one in force. Filtering and
174
204
  # ordering are the same kind of control — pick one of a handful — so
@@ -4,8 +4,8 @@
4
4
  What narrows a list, and the list. A search that filters as you type, and
5
5
  a register for each other way of narrowing it: the same two positions
6
6
  every time, a quiet label and the choices, the one in force carrying the
7
- accent and the weight. Under it, cards on the page's own fields — this
8
- page says a card is two fields wide.
7
+ weight. Under it, cards on the page's own fields — this page says a card
8
+ is two fields wide.
9
9
  </p>
10
10
 
11
11
  <div class="filters">
@@ -12,4 +12,22 @@
12
12
  <a href="#page-head" class="button">New from this</a>
13
13
  <a href="#page-head" class="button button--danger">Delete palette</a>
14
14
  <% end %>
15
+
16
+ <h4>Sections</h4>
17
+ <p class="hint">
18
+ A page with several surfaces names them under the title, the one shown
19
+ in the weight, and shows that one alone. What is done daily is reached
20
+ from the head, never by scrolling past what was done yesterday.
21
+ </p>
22
+ <%= page_head "Deck Chair", sections: [ [ "Compose", "#page-head", true ], [ "Dress", "#page-head", false ], [ "Export", "#page-head", false ] ] do %>
23
+ <a href="#page-head" class="button button--primary">Dress this pattern</a>
24
+ <a href="#page-head" class="button button--quiet">Rename</a>
25
+ <% end %>
26
+
27
+ <h4>Explanations</h4>
28
+ <p class="hint">
29
+ What a section means, behind one mark, for the day it is first read.
30
+ Closed, in the hint register, and there when it is asked for.
31
+ </p>
32
+ <%= explain "The repeat, in order along the stripe normal. Widths sum to one." %>
15
33
  </section>
@@ -1,3 +1,3 @@
1
1
  module ItsSwiss
2
- VERSION = "0.8.0"
2
+ VERSION = "0.9.1"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: its-swiss
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bobby Meyer