Seg-tabs take buttons for a choice made in place, pressed by aria-pressed
A choice that changes the page in place, like a list or grid view, is a button: there is no address to link to, and a script moves the pressed state. 1.0 documents only links in a seg-tabs track and leaves a button in one with the browser's face, grey, bordered and in the system font beside the tabs. So every seg-tabs rule that names a link now names the track's own buttons too (the shape, hover, the desktop floor, the phone's centring, the icon rule, forced colours and print), the pressed button (aria-pressed="true") is the thumb the current link is, and one reset rule takes the browser's button face away. Links stay right where a choice has an address: they work with no script. The rules use the child combinator, so a button nested deeper in a track, which a 1.0 page could carry inside some other control, keeps the look 1.0 gave it. The promise gates hold this: the six button selectors are newOnOldMarkup's exact entries, and the icon rule's two :where() lists are widenings that add one argument each. Written with a descendant combinator, the hover rule is refused by the new-rule gate (shown on a scratch copy). TestASegTabButtonLooksLikeASegTabLink puts both kinds in one track and reads the computed face, in every theme and scheme, with a mouse at 1280 and on touch at 390: the pressed button against the current link, the unpressed one against a plain link (no border, no fill), hovered against a hovered link, and the pressed button under forced colours and in print. It was red by assertion against the old CSS in every leg. The tap inventory gains a row for the buttons, the forced colours and print gate names the pressed button in both blocks, and the sizing and class-spelling fixtures carry a button track, so the tap drive measures two buttons by count and the spelling drive compares the button rules in both spellings. The new state, "Buttons that switch in place", and the changed seg-tabs blurb and new state note are the approved English, byte for byte. Their translations in the eleven gallery catalogs, and the sample words View, List and Grid, are machine drafts from that English, not reviewed; drafts.json names each. The group's aria-label "View" names the view a reader picks, but the sample catalog's bare View is a row menu's link, a verb in most of the eleven (Russian Открыть, open), which would label the group as a command. The localiser gains a noun sense beside its verb sense, and seg-tabs looks View up as "View (noun)" (Вид). TestANounAndItsVerbAreTranslatedApart was red without that entry. Size, gzip level 6 (allotment 140, on target): fiesta.css 23,146 -> 23,239 (+93) fiesta-plain.css 22,431 -> 22,524 (+93) fiesta-signal.css 22,542 -> 22,636 (+94)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
39 files changed,
+418
−29
check/baseline_test.go+29 −1check/depth_media_test.go+2 −2check/segtabs_browser_test.go+154 −0check/sizing_browser_test.go+1 −0check/sizing_test.go+1 −0check/testdata/fixtures/sizing.html+1 −1check/testdata/fixtures/spelling-class.html+1 −0css/tokens.css+18 −8examples/examples.json+5 −0examples/prose.json+4 −1examples/seg-tabs/buttons-that-switch-in-place.html+13 −0hack/test-weights.txt+1 −0internal/gallery/heights.go+2 −0internal/gallery/i18n.go+1 −2internal/gallery/i18n/ar.json+3 −1internal/gallery/i18n/bn.json+3 −1internal/gallery/i18n/drafts.json+81 −1internal/gallery/i18n/es.json+3 −1internal/gallery/i18n/ga.json+3 −1internal/gallery/i18n/hi.json+3 −1internal/gallery/i18n/ja.json+3 −1internal/gallery/i18n/pt-PT.json+3 −1internal/gallery/i18n/ru.json+3 −1internal/gallery/i18n/samples.ar.json+3 −0internal/gallery/i18n/samples.bn.json+3 −0internal/gallery/i18n/samples.es.json+3 −0internal/gallery/i18n/samples.ga.json+3 −0internal/gallery/i18n/samples.hi.json+3 −0internal/gallery/i18n/samples.ja.json+3 −0internal/gallery/i18n/samples.pt-PT.json+3 −0internal/gallery/i18n/samples.ru.json+3 −0internal/gallery/i18n/samples.vi.json+3 −0internal/gallery/i18n/samples.yue.json+3 −0internal/gallery/i18n/samples.zh-Hans.json+3 −0internal/gallery/i18n/vi.json+3 −1internal/gallery/i18n/yue.json+3 −1internal/gallery/i18n/zh-Hans.json+3 −1internal/gallery/localise.go+16 −2internal/gallery/localise_test.go+22 −0
CI passed — run details
CI log
=== gofmt ===
=== no-rastrillo ===
go test -run '^(TestFiestaNeverImportsRastrillo|TestThePublicPackagesLinkNoBrowser)$' -count=1 .
ok amadan.net/rastrillo/fiesta 0.220s
=== size ===
go test ./internal/build -run '^TestTheOneLinkIsUnder25KiBGzipped$' -count=1 -v
=== RUN TestTheOneLinkIsUnder25KiBGzipped
build_test.go:138: fiesta-plain.css: 154420 bytes, 22524 bytes gzipped (22.0 KiB of the 25.0 KiB limit)
build_test.go:138: fiesta-signal.css: 154515 bytes, 22636 bytes gzipped (22.1 KiB of the 25.0 KiB limit)
build_test.go:138: fiesta.css: 157334 bytes, 23239 bytes gzipped (22.7 KiB of the 25.0 KiB limit)
--- PASS: TestTheOneLinkIsUnder25KiBGzipped (0.02s)
PASS
ok amadan.net/rastrillo/fiesta/internal/build 0.025s
=== history ===
history: 24 commits; release tags: v1.0.0
=== suite ===
--- shard 0 of 48 ---
==> shard 0 of 48: make root
go build ./...
go vet ./...
FIESTA_TEST_REQUIRE_NODE=1 FIESTA_TEST_REQUIRE_GIT=1 FIESTA_TEST_REQUIRE_TAGS=1 ./hack/gotest.sh root 'internal/gallery@3 cmd/fiesta@2 internal/www@2'
ok amadan.net/rastrillo/fiesta 2.508s
ok amadan.net/rastrillo/fiesta/check 7.279s
ok amadan.net/rastrillo/fiesta/example 0.151s
? amadan.net/rastrillo/fiesta/hack/seed/english [no test files]
ok amadan.net/rastrillo/fiesta/internal/axe 0.144s
ok amadan.net/rastrillo/fiesta/internal/build 0.875s
ok amadan.net/rastrillo/fiesta/internal/codeview 0.008s
? amadan.net/rastrillo/fiesta/internal/harness [no test files]
ok amadan.net/rastrillo/fiesta/internal/markup 0.006s
ok amadan.net/rastrillo/fiesta/internal/nodetest 0.358s
ok amadan.net/rastrillo/fiesta/internal/site 2.368s
ok amadan.net/rastrillo/fiesta/internal/tokens 0.016s
suite: root took 16s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m11.760000s 0m1.980000s
--- shard 1 of 48 ---
==> shard 1 of 48: make test.internal.gallery~1of3
FIESTA_TEST_REQUIRE_NODE=1 FIESTA_TEST_REQUIRE_GIT=1 FIESTA_TEST_REQUIRE_TAGS=1 ./hack/gotest.sh test 'internal.gallery~1of3'
gotest: slice 1 of 3 of ./internal/gallery/ runs 36 of 108 test tests
ok amadan.net/rastrillo/fiesta/internal/gallery 63.950s
suite: test.internal.gallery~1of3 took 71s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m49.920000s 0m1.280000s
--- shard 2 of 48 ---
==> shard 2 of 48: make test.internal.gallery~2of3
FIESTA_TEST_REQUIRE_NODE=1 FIESTA_TEST_REQUIRE_GIT=1 FIESTA_TEST_REQUIRE_TAGS=1 ./hack/gotest.sh test 'internal.gallery~2of3'
gotest: slice 2 of 3 of ./internal/gallery/ runs 36 of 108 test tests
ok amadan.net/rastrillo/fiesta/internal/gallery 66.697s
suite: test.internal.gallery~2of3 took 73s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m52.620000s 0m1.300000s
--- shard 3 of 48 ---
==> shard 3 of 48: make test.internal.gallery~3of3
FIESTA_TEST_REQUIRE_NODE=1 FIESTA_TEST_REQUIRE_GIT=1 FIESTA_TEST_REQUIRE_TAGS=1 ./hack/gotest.sh test 'internal.gallery~3of3'
gotest: slice 3 of 3 of ./internal/gallery/ runs 36 of 108 test tests
ok amadan.net/rastrillo/fiesta/internal/gallery 62.295s
suite: test.internal.gallery~3of3 took 69s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m48.090000s 0m1.510000s
--- shard 4 of 48 ---
==> shard 4 of 48: make test.cmd.fiesta~1of2
FIESTA_TEST_REQUIRE_NODE=1 FIESTA_TEST_REQUIRE_GIT=1 FIESTA_TEST_REQUIRE_TAGS=1 ./hack/gotest.sh test 'cmd.fiesta~1of2'
gotest: slice 1 of 2 of ./cmd/fiesta/ runs 7 of 12 test tests
ok amadan.net/rastrillo/fiesta/cmd/fiesta 53.821s
suite: test.cmd.fiesta~1of2 took 59s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m32.670000s 0m2.080000s
--- shard 5 of 48 ---
==> shard 5 of 48: make test.cmd.fiesta~2of2
FIESTA_TEST_REQUIRE_NODE=1 FIESTA_TEST_REQUIRE_GIT=1 FIESTA_TEST_REQUIRE_TAGS=1 ./hack/gotest.sh test 'cmd.fiesta~2of2'
gotest: slice 2 of 2 of ./cmd/fiesta/ runs 5 of 12 test tests
ok amadan.net/rastrillo/fiesta/cmd/fiesta 57.654s
suite: test.cmd.fiesta~2of2 took 63s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m35.320000s 0m2.320000s
--- shard 6 of 48 ---
==> shard 6 of 48: make test.internal.www~1of2
FIESTA_TEST_REQUIRE_NODE=1 FIESTA_TEST_REQUIRE_GIT=1 FIESTA_TEST_REQUIRE_TAGS=1 ./hack/gotest.sh test 'internal.www~1of2'
gotest: slice 1 of 2 of ./internal/www/ runs 11 of 25 test tests
ok amadan.net/rastrillo/fiesta/internal/www 61.882s
suite: test.internal.www~1of2 took 69s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m46.930000s 0m1.710000s
--- shard 7 of 48 ---
==> shard 7 of 48: make test.internal.www~2of2
FIESTA_TEST_REQUIRE_NODE=1 FIESTA_TEST_REQUIRE_GIT=1 FIESTA_TEST_REQUIRE_TAGS=1 ./hack/gotest.sh test 'internal.www~2of2'
gotest: slice 2 of 2 of ./internal/www/ runs 14 of 25 test tests
ok amadan.net/rastrillo/fiesta/internal/www 58.744s
suite: test.internal.www~2of2 took 65s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m44.670000s 0m1.600000s
--- shard 8 of 48 ---
==> shard 8 of 48: make staticcheck
go run honnef.co/go/tools/cmd/staticcheck@v0.8.2 -tags browser ./...
suite: staticcheck took 12s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m14.410000s 0m0.900000s
--- shard 9 of 48 ---
==> shard 9 of 48: make govulncheck
go run golang.org/x/vuln/cmd/govulncheck@v1.8.0 ./...
=== Symbol Results ===
No vulnerabilities found.
Your code is affected by 0 vulnerabilities.
This scan also found 2 vulnerabilities in packages you import and 5
vulnerabilities in modules you require, but your code doesn't appear to call
these vulnerabilities.
Use '-show verbose' for more details.
suite: govulncheck took 8s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m3.270000s 0m0.350000s
--- shard 10 of 48 ---
==> shard 10 of 48: make gitleaks
go run github.com/zricethezav/gitleaks/v8@v8.30.1 git --no-banner --redact --log-opts='--full-history --diff-filter=tuxdb HEAD' .
[90m4:49AM[0m [32mINF[0m [1mUnknown SCM platform. Use --platform to include links in findings.[0m [36mhost=[0mamadan.net
[90m4:49AM[0m [32mINF[0m [1m24 commits scanned.[0m
[90m4:49AM[0m [32mINF[0m [1mscanned ~8313339 bytes (8.31 MB) in 7.07s[0m
[90m4:49AM[0m [32mINF[0m [1mno leaks found[0m
suite: gitleaks took 9s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m6.880000s 0m0.240000s
--- shard 11 of 48 ---
==> shard 11 of 48: make browser.internal.harness
./hack/gotest.sh browser 'internal.harness'
gotest: on ip-172-31-31-16, driving the harness's own choice (/usr/local/bin/chromium)
gotest: ./internal/harness/ runs its 10 browser tests
ok amadan.net/rastrillo/fiesta/internal/harness 4.836s
suite: browser.internal.harness took 11s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m1.670000s 0m1.320000s
--- shard 12 of 48 ---
==> shard 12 of 48: make browser.check~1of13
./hack/gotest.sh browser 'check~1of13'
gotest: on ip-172-31-31-16, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 1 of 13 of ./check/ runs 16 of 217 browser tests
ok amadan.net/rastrillo/fiesta/check 89.684s
suite: browser.check~1of13 took 100s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m48.570000s 0m14.280000s
--- shard 13 of 48 ---
==> shard 13 of 48: make browser.check~2of13
./hack/gotest.sh browser 'check~2of13'
gotest: on ip-172-31-31-16, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 2 of 13 of ./check/ runs 16 of 217 browser tests
ok amadan.net/rastrillo/fiesta/check 92.407s
suite: browser.check~2of13 took 103s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m51.580000s 0m13.460000s
--- shard 14 of 48 ---
==> shard 14 of 48: make browser.check~3of13
./hack/gotest.sh browser 'check~3of13'
gotest: on ip-172-31-31-16, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 3 of 13 of ./check/ runs 16 of 217 browser tests
ok amadan.net/rastrillo/fiesta/check 89.850s
suite: browser.check~3of13 took 100s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m41.780000s 0m11.190000s
--- shard 15 of 48 ---
==> shard 15 of 48: make browser.check~4of13
./hack/gotest.sh browser 'check~4of13'
gotest: on ip-172-31-31-16, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 4 of 13 of ./check/ runs 16 of 217 browser tests
ok amadan.net/rastrillo/fiesta/check 88.539s
suite: browser.check~4of13 took 99s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m52.640000s 0m15.850000s
--- shard 16 of 48 ---
==> shard 16 of 48: make browser.check~5of13
./hack/gotest.sh browser 'check~5of13'
gotest: on ip-172-31-31-16, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 5 of 13 of ./check/ runs 17 of 217 browser tests
ok amadan.net/rastrillo/fiesta/check 80.349s
suite: browser.check~5of13 took 90s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m38.990000s 0m13.740000s
--- shard 17 of 48 ---
==> shard 17 of 48: make browser.check~6of13
./hack/gotest.sh browser 'check~6of13'
gotest: on ip-172-31-31-16, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 6 of 13 of ./check/ runs 17 of 217 browser tests
ok amadan.net/rastrillo/fiesta/check 77.218s
suite: browser.check~6of13 took 87s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m43.600000s 0m14.590000s
--- shard 18 of 48 ---
==> shard 18 of 48: make browser.check~7of13
./hack/gotest.sh browser 'check~7of13'
gotest: on ip-172-31-31-16, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 7 of 13 of ./check/ runs 17 of 217 browser tests
ok amadan.net/rastrillo/fiesta/check 84.834s
suite: browser.check~7of13 took 95s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m44.320000s 0m14.070000s
--- shard 19 of 48 ---
==> shard 19 of 48: make browser.check~8of13
./hack/gotest.sh browser 'check~8of13'
gotest: on ip-172-31-31-16, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 8 of 13 of ./check/ runs 17 of 217 browser tests
ok amadan.net/rastrillo/fiesta/check 91.005s
suite: browser.check~8of13 took 101s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m46.460000s 0m16.000000s
--- shard 20 of 48 ---
==> shard 20 of 48: make browser.check~9of13
./hack/gotest.sh browser 'check~9of13'
gotest: on ip-172-31-31-16, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 9 of 13 of ./check/ runs 17 of 217 browser tests
ok amadan.net/rastrillo/fiesta/check 75.711s
suite: browser.check~9of13 took 86s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m30.920000s 0m9.310000s
--- shard 21 of 48 ---
==> shard 21 of 48: make browser.check~10of13
./hack/gotest.sh browser 'check~10of13'
gotest: on ip-172-31-31-16, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 10 of 13 of ./check/ runs 17 of 217 browser tests
ok amadan.net/rastrillo/fiesta/check 82.264s
suite: browser.check~10of13 took 92s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m39.070000s 0m12.630000s
--- shard 22 of 48 ---
==> shard 22 of 48: make browser.check~11of13
./hack/gotest.sh browser 'check~11of13'
gotest: on ip-172-31-31-16, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 11 of 13 of ./check/ runs 17 of 217 browser tests
ok amadan.net/rastrillo/fiesta/check 85.944s
suite: browser.check~11of13 took 96s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m43.750000s 0m14.430000s
--- shard 23 of 48 ---
==> shard 23 of 48: make browser.check~12of13
./hack/gotest.sh browser 'check~12of13'
gotest: on ip-172-31-31-16, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 12 of 13 of ./check/ runs 17 of 217 browser tests
ok amadan.net/rastrillo/fiesta/check 84.272s
suite: browser.check~12of13 took 94s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m44.490000s 0m15.440000s
--- shard 24 of 48 ---
==> shard 24 of 48: make browser.check~13of13
./hack/gotest.sh browser 'check~13of13'
gotest: on ip-172-31-31-16, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 13 of 13 of ./check/ runs 17 of 217 browser tests
ok amadan.net/rastrillo/fiesta/check 80.723s
suite: browser.check~13of13 took 91s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m39.410000s 0m11.520000s
--- shard 25 of 48 ---
==> shard 25 of 48: make browser.internal.gallery~1of18
./hack/gotest.sh browser 'internal.gallery~1of18'
gotest: on ip-172-31-31-16, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 1 of 18 of ./internal/gallery/ runs 5 of 84 browser tests
ok amadan.net/rastrillo/fiesta/internal/gallery 39.536s
suite: browser.internal.gallery~1of18 took 49s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m16.220000s 0m3.860000s
--- shard 26 of 48 ---
==> shard 26 of 48: make browser.internal.gallery~2of18
./hack/gotest.sh browser 'internal.gallery~2of18'
gotest: on ip-172-31-31-16, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 2 of 18 of ./internal/gallery/ runs 5 of 84 browser tests
ok amadan.net/rastrillo/fiesta/internal/gallery 49.767s
suite: browser.internal.gallery~2of18 took 59s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m14.140000s 0m2.750000s
--- shard 27 of 48 ---
==> shard 27 of 48: make browser.internal.gallery~3of18
./hack/gotest.sh browser 'internal.gallery~3of18'
gotest: on ip-172-31-31-16, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 3 of 18 of ./internal/gallery/ runs 5 of 84 browser tests
ok amadan.net/rastrillo/fiesta/internal/gallery 60.273s
suite: browser.internal.gallery~3of18 took 70s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m16.580000s 0m4.270000s
--- shard 28 of 48 ---
==> shard 28 of 48: make browser.internal.gallery~4of18
./hack/gotest.sh browser 'internal.gallery~4of18'
gotest: on ip-172-31-31-16, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 4 of 18 of ./internal/gallery/ runs 6 of 84 browser tests
ok amadan.net/rastrillo/fiesta/internal/gallery 50.861s
suite: browser.internal.gallery~4of18 took 60s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m14.160000s 0m2.880000s
--- shard 29 of 48 ---
==> shard 29 of 48: make browser.internal.gallery~5of18
./hack/gotest.sh browser 'internal.gallery~5of18'
gotest: on ip-172-31-31-16, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 5 of 18 of ./internal/gallery/ runs 6 of 84 browser tests
ok amadan.net/rastrillo/fiesta/internal/gallery 59.443s
suite: browser.internal.gallery~5of18 took 69s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m18.790000s 0m5.320000s
--- shard 30 of 48 ---
==> shard 30 of 48: make browser.internal.gallery~6of18
./hack/gotest.sh browser 'internal.gallery~6of18'
gotest: on ip-172-31-31-16, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 6 of 18 of ./internal/gallery/ runs 5 of 84 browser tests
ok amadan.net/rastrillo/fiesta/internal/gallery 58.214s
suite: browser.internal.gallery~6of18 took 68s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m16.640000s 0m4.120000s
--- shard 31 of 48 ---
==> shard 31 of 48: make browser.internal.gallery~7of18
./hack/gotest.sh browser 'internal.gallery~7of18'
gotest: on ip-172-31-31-16, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 7 of 18 of ./internal/gallery/ runs 5 of 84 browser tests
ok amadan.net/rastrillo/fiesta/internal/gallery 56.480s
suite: browser.internal.gallery~7of18 took 66s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m14.200000s 0m2.910000s
--- shard 32 of 48 ---
==> shard 32 of 48: make browser.internal.gallery~8of18
./hack/gotest.sh browser 'internal.gallery~8of18'
gotest: on ip-172-31-31-16, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 8 of 18 of ./internal/gallery/ runs 5 of 84 browser tests
ok amadan.net/rastrillo/fiesta/internal/gallery 59.267s
suite: browser.internal.gallery~8of18 took 69s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m18.180000s 0m4.740000s
--- shard 33 of 48 ---
==> shard 33 of 48: make browser.internal.gallery~9of18
./hack/gotest.sh browser 'internal.gallery~9of18'
gotest: on ip-172-31-31-16, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 9 of 18 of ./internal/gallery/ runs 6 of 84 browser tests
ok amadan.net/rastrillo/fiesta/internal/gallery 66.940s
suite: browser.internal.gallery~9of18 took 76s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m23.480000s 0m7.540000s
--- shard 34 of 48 ---
==> shard 34 of 48: make browser.internal.gallery~10of18
./hack/gotest.sh browser 'internal.gallery~10of18'
gotest: on ip-172-31-31-16, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 10 of 18 of ./internal/gallery/ runs 2 of 84 browser tests
ok amadan.net/rastrillo/fiesta/internal/gallery 61.697s
suite: browser.internal.gallery~10of18 took 71s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m14.360000s 0m2.790000s
--- shard 35 of 48 ---
==> shard 35 of 48: make browser.internal.gallery~11of18
./hack/gotest.sh browser 'internal.gallery~11of18'
gotest: on ip-172-31-31-16, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 11 of 18 of ./internal/gallery/ runs 2 of 84 browser tests
ok amadan.net/rastrillo/fiesta/internal/gallery 62.592s
suite: browser.internal.gallery~11of18 took 72s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m14.240000s 0m2.840000s
--- shard 36 of 48 ---
==> shard 36 of 48: make browser.internal.gallery~12of18
./hack/gotest.sh browser 'internal.gallery~12of18'
gotest: on ip-172-31-31-16, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 12 of 18 of ./internal/gallery/ runs 2 of 84 browser tests
ok amadan.net/rastrillo/fiesta/internal/gallery 64.611s
suite: browser.internal.gallery~12of18 took 74s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m19.070000s 0m4.860000s
--- shard 37 of 48 ---
==> shard 37 of 48: make browser.internal.gallery~13of18
./hack/gotest.sh browser 'internal.gallery~13of18'
gotest: on ip-172-31-31-16, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 13 of 18 of ./internal/gallery/ runs 4 of 84 browser tests
ok amadan.net/rastrillo/fiesta/internal/gallery 65.775s
suite: browser.internal.gallery~13of18 took 75s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m19.450000s 0m5.520000s
--- shard 38 of 48 ---
==> shard 38 of 48: make browser.internal.gallery~14of18
./hack/gotest.sh browser 'internal.gallery~14of18'
gotest: on ip-172-31-31-16, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 14 of 18 of ./internal/gallery/ runs 5 of 84 browser tests
ok amadan.net/rastrillo/fiesta/internal/gallery 68.555s
suite: browser.internal.gallery~14of18 took 78s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m19.590000s 0m5.650000s
--- shard 39 of 48 ---
==> shard 39 of 48: make browser.internal.gallery~15of18
./hack/gotest.sh browser 'internal.gallery~15of18'
gotest: on ip-172-31-31-16, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 15 of 18 of ./internal/gallery/ runs 5 of 84 browser tests
ok amadan.net/rastrillo/fiesta/internal/gallery 68.053s
suite: browser.internal.gallery~15of18 took 78s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m19.850000s 0m5.620000s
--- shard 40 of 48 ---
==> shard 40 of 48: make browser.internal.gallery~16of18
./hack/gotest.sh browser 'internal.gallery~16of18'
gotest: on ip-172-31-31-16, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 16 of 18 of ./internal/gallery/ runs 5 of 84 browser tests
ok amadan.net/rastrillo/fiesta/internal/gallery 63.994s
suite: browser.internal.gallery~16of18 took 73s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m16.780000s 0m4.460000s
--- shard 41 of 48 ---
==> shard 41 of 48: make browser.internal.gallery~17of18
./hack/gotest.sh browser 'internal.gallery~17of18'
gotest: on ip-172-31-31-16, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 17 of 18 of ./internal/gallery/ runs 5 of 84 browser tests
ok amadan.net/rastrillo/fiesta/internal/gallery 69.878s
suite: browser.internal.gallery~17of18 took 79s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m19.850000s 0m5.790000s
--- shard 42 of 48 ---
==> shard 42 of 48: make browser.internal.gallery~18of18
./hack/gotest.sh browser 'internal.gallery~18of18'
gotest: on ip-172-31-31-16, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 18 of 18 of ./internal/gallery/ runs 6 of 84 browser tests
ok amadan.net/rastrillo/fiesta/internal/gallery 69.868s
suite: browser.internal.gallery~18of18 took 79s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m21.100000s 0m6.560000s
--- shard 43 of 48 ---
==> shard 43 of 48: make browser.internal.gallery.history
./hack/gotest.sh browser 'internal.gallery.history'
gotest: on ip-172-31-31-16, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: ./internal/gallery/history/ runs its 3 browser tests
ok amadan.net/rastrillo/fiesta/internal/gallery/history 32.781s
suite: browser.internal.gallery.history took 48s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m21.470000s 0m2.200000s
--- shard 44 of 48 ---
==> shard 44 of 48: make browser.internal.www~1of4
./hack/gotest.sh browser 'internal.www~1of4'
gotest: on ip-172-31-31-16, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 1 of 4 of ./internal/www/ runs 4 of 22 browser tests
ok amadan.net/rastrillo/fiesta/internal/www 69.946s
suite: browser.internal.www~1of4 took 78s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m31.300000s 0m2.190000s
--- shard 45 of 48 ---
==> shard 45 of 48: make browser.internal.www~2of4
./hack/gotest.sh browser 'internal.www~2of4'
gotest: on ip-172-31-31-16, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 2 of 4 of ./internal/www/ runs 6 of 22 browser tests
ok amadan.net/rastrillo/fiesta/internal/www 67.735s
suite: browser.internal.www~2of4 took 76s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m45.800000s 0m2.770000s
--- shard 46 of 48 ---
==> shard 46 of 48: make browser.internal.www~3of4
./hack/gotest.sh browser 'internal.www~3of4'
gotest: on ip-172-31-31-16, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 3 of 4 of ./internal/www/ runs 6 of 22 browser tests
ok amadan.net/rastrillo/fiesta/internal/www 81.134s
suite: browser.internal.www~3of4 took 89s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m51.800000s 0m3.050000s
--- shard 47 of 48 ---
==> shard 47 of 48: make browser.internal.www~4of4
./hack/gotest.sh browser 'internal.www~4of4'
gotest: on ip-172-31-31-16, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 4 of 4 of ./internal/www/ runs 6 of 22 browser tests
ok amadan.net/rastrillo/fiesta/internal/www 77.582s
suite: browser.internal.www~4of4 took 86s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m45.920000s 0m3.070000s
Intent
diff --git a/check/baseline_test.go b/check/baseline_test.go| index aacb8f4..b138c45 100644 |
| --- a/check/baseline_test.go |
| +++ b/check/baseline_test.go |
| @@ -62,10 +62,25 @@ type widening struct { |
| // the rule already styles changes weight. |
| const footFlat = "the footer row's menus are flat, as a shell tail's are" |
| +// A seg-tabs button is a control as a seg-tabs link is, so the rule |
| +// that sizes an icon inside a control names it too. :where() weighs |
| +// nothing, so no icon 1.0 already sizes changes. |
| +const segTabButtonIcons = "a seg-tabs button is a control, so an icon in it is sized as one" |
| + |
| // widenedSelectors is keyed by the 1.0 selector. Each entry must be |
| // used: an entry for a selector the CSS no longer widens is a promise |
| // about nothing. |
| var widenedSelectors = map[string]widening{ |
| + ":where(.rst-btn, .rst-search, .rst-search__clear, .rst-ftok a, .rst-help, .rst-dropdown > summary, .rst-dropdown__menu a, .rst-dropdown__menu button, .rst-dropdown__menu .rst-menu-group > summary, .rst-row-menu__panel a, .rst-row-menu__panel button, .rst-locale button, .rst-scheme button, .rst-combo__option, .rst-dtp__row, .rst-dtp__pick, .rst-cal__nav, .rst-row__action, .rst-row-menu > summary, .rst-pagination > a, .rst-seg-tabs a, .rst-switch, .rst-choice__cards label, .rst-tblock__head, .rst-bulkbar__close, .rst-bulkbar__escalate, .rst-modal-close, .rst-modal-panel > nav a, .rst-back-nav a, .rst-signin__providers > a, .rst-shell__brand, .rst-shell__nav a, .rst-shell__menu > summary, .rst-shell__chrome > summary, .rst-shell__account > summary, .rst-shell__back > a) .icon": { |
| + Now: ":where(.rst-btn, .rst-search, .rst-search__clear, .rst-ftok a, .rst-help, .rst-dropdown > summary, .rst-dropdown__menu a, .rst-dropdown__menu button, .rst-dropdown__menu .rst-menu-group > summary, .rst-row-menu__panel a, .rst-row-menu__panel button, .rst-locale button, .rst-scheme button, .rst-combo__option, .rst-dtp__row, .rst-dtp__pick, .rst-cal__nav, .rst-row__action, .rst-row-menu > summary, .rst-pagination > a, .rst-seg-tabs a, .rst-seg-tabs > button, .rst-switch, .rst-choice__cards label, .rst-tblock__head, .rst-bulkbar__close, .rst-bulkbar__escalate, .rst-modal-close, .rst-modal-panel > nav a, .rst-back-nav a, .rst-signin__providers > a, .rst-shell__brand, .rst-shell__nav a, .rst-shell__menu > summary, .rst-shell__chrome > summary, .rst-shell__account > summary, .rst-shell__back > a) .icon", |
| + Added: []string{".rst-seg-tabs > button"}, |
| + Why: segTabButtonIcons, |
| + }, |
| + ":where([rst-btn], [rst-search], [rst-search-clear], [rst-ftok] a, [rst-help], [rst-dropdown] > summary, [rst-dropdown-menu] a, [rst-dropdown-menu] button, [rst-dropdown-menu] [rst-menu-group] > summary, [rst-row-menu-panel] a, [rst-row-menu-panel] button, [rst-locale] button, [rst-scheme] button, [rst-combo-option], [rst-dtp-row], [rst-dtp-pick], [rst-cal-nav], [rst-row-action], [rst-row-menu] > summary, [rst-pagination] > a, [rst-seg-tabs] a, [rst-switch], [rst-choice-cards] label, [rst-tblock-head], [rst-bulkbar-close], [rst-bulkbar-escalate], [rst-modal-close], [rst-modal-panel] > nav a, [rst-back-nav] a, [rst-signin-providers] > a, [rst-shell-brand], [rst-shell-nav] a, [rst-shell-menu] > summary, [rst-shell-chrome] > summary, [rst-shell-account] > summary, [rst-shell-back] > a) .icon": { |
| + Now: ":where([rst-btn], [rst-search], [rst-search-clear], [rst-ftok] a, [rst-help], [rst-dropdown] > summary, [rst-dropdown-menu] a, [rst-dropdown-menu] button, [rst-dropdown-menu] [rst-menu-group] > summary, [rst-row-menu-panel] a, [rst-row-menu-panel] button, [rst-locale] button, [rst-scheme] button, [rst-combo-option], [rst-dtp-row], [rst-dtp-pick], [rst-cal-nav], [rst-row-action], [rst-row-menu] > summary, [rst-pagination] > a, [rst-seg-tabs] a, [rst-seg-tabs] > button, [rst-switch], [rst-choice-cards] label, [rst-tblock-head], [rst-bulkbar-close], [rst-bulkbar-escalate], [rst-modal-close], [rst-modal-panel] > nav a, [rst-back-nav] a, [rst-signin-providers] > a, [rst-shell-brand], [rst-shell-nav] a, [rst-shell-menu] > summary, [rst-shell-chrome] > summary, [rst-shell-account] > summary, [rst-shell-back] > a) .icon", |
| + Added: []string{"[rst-seg-tabs] > button"}, |
| + Why: segTabButtonIcons, |
| + }, |
| ".rst-dropdown:not(.rst-shell__account, .rst-shell__profile, .rst-shell__tail .rst-dropdown, .rst-bulkbar .rst-dropdown, .rst-dropdown__menu .rst-dropdown) > summary:not(.rst-btn)": { |
| Now: ".rst-dropdown:not(.rst-shell__account, .rst-shell__profile, .rst-shell__tail .rst-dropdown, .rst-page__foot .rst-dropdown, .rst-bulkbar .rst-dropdown, .rst-dropdown__menu .rst-dropdown) > summary:not(.rst-btn)", |
| Added: []string{".rst-page__foot .rst-dropdown"}, |
| @@ -113,7 +128,20 @@ var widenedSelectors = map[string]widening{ |
| // seg-tabs track, which 1.0 documents only as links and leaves |
| // unstyled. Compared after parsing, compound by compound, never as a |
| // substring, so a selector that merely contains an entry is not it. |
| -var newOnOldMarkup = map[string]string{} |
| +var newOnOldMarkup = map[string]string{ |
| + "[rst-seg-tabs] > button": segTabButton, |
| + "[rst-seg-tabs] > button:hover": segTabButton, |
| + `[rst-seg-tabs] > button[aria-pressed="true"]`: segTabButton, |
| + ".rst-seg-tabs > button": segTabButton, |
| + ".rst-seg-tabs > button:hover": segTabButton, |
| + `.rst-seg-tabs > button[aria-pressed="true"]`: segTabButton, |
| +} |
| + |
| +// segTabButton is each entry's why. Every entry uses the child |
| +// combinator, so it reaches only a track's own buttons: a button nested |
| +// deeper, inside some other control a 1.0 page put in a track, is not |
| +// a tab and keeps the look 1.0 gave it. |
| +const segTabButton = "1.0 documents only links in a seg-tabs track and leaves a button in one unstyled" |
| // declaration is one thing a stylesheet says, read in the context that |
| // decides when it applies: the chain of grouping rules around it, one |
diff --git a/check/depth_media_test.go b/check/depth_media_test.go| index 7f0c790..5762cf4 100644 |
| --- a/check/depth_media_test.go |
| +++ b/check/depth_media_test.go |
| @@ -30,11 +30,11 @@ func TestForcedColoursAndPrintFlattenTheAxisAndCoverEveryComponent(t *testing.T) |
| want []string |
| }{ |
| {"@media (forced-colors: active) {", append(flat, "html:root {", "[rst-btn]", "[rst-box]", "[rst-card]", "[rst-stats]", "[rst-list]", "[rst-input]", "[rst-textarea]", "[rst-search]", "[rst-dropdown]", "[rst-switch-track]", "[rst-switch-track]::after", |
| - "[rst-shell-sidebar] [rst-shell-nav] a[aria-current]", "[rst-shell-console] [rst-shell-nav] a[aria-current]", "[rst-shell-topbar] [rst-shell-tail] > [rst-shell-nav] a[aria-current]", "[rst-dropdown-menu] a[aria-current]", "[rst-pagination] [aria-current=\"page\"]", "[rst-seg-tabs] a[aria-current]", |
| + "[rst-shell-sidebar] [rst-shell-nav] a[aria-current]", "[rst-shell-console] [rst-shell-nav] a[aria-current]", "[rst-shell-topbar] [rst-shell-tail] > [rst-shell-nav] a[aria-current]", "[rst-dropdown-menu] a[aria-current]", "[rst-pagination] [aria-current=\"page\"]", "[rst-seg-tabs] a[aria-current]", `[rst-seg-tabs] > button[aria-pressed="true"]`, |
| `[rst-shell-sidebar][rst-shell-sidebar~="index"] [rst-shell-nav] > a[aria-current]:hover`, `[rst-shell-console][rst-shell-console~="index"] [rst-shell-nav] > a[aria-current]:active`, |
| "ButtonBorder", "CanvasText", "ButtonText", "Highlight", "HighlightText", "box-shadow: none")}, |
| {"@media print {", append(flat, "html:root {", "--rst-accent-fill: transparent", "--rst-accent-fill-hover: transparent", "--rst-on-accent-fill: var(--rst-text)", "--rst-danger-fill: transparent", "--rst-on-danger-fill-hover: var(--rst-text)", "--rst-danger-hover-filter: none", "--rst-control-face-hover: transparent", "--rst-control-text-hover: var(--rst-text)", "--rst-knob: var(--rst-text)", |
| - "[rst-btn]", "[rst-box]", "[rst-card]", "[rst-stats]", "[rst-list]", "[rst-input]", "[rst-textarea]", "[rst-search]", "[rst-dropdown]", "[rst-switch-track]", "[rst-switch-track]::after", "[rst-switch]:has(input:checked) [rst-switch-track]", "[rst-tblock] > [rst-tblock-head]:has(> input:checked) > [rst-switch-track]", "[rst-seg-tabs] a[aria-current]", "[rst-pagination] [aria-current=\"page\"]", "[rst-dropdown-menu]", "[rst-row-menu-panel]", |
| + "[rst-btn]", "[rst-box]", "[rst-card]", "[rst-stats]", "[rst-list]", "[rst-input]", "[rst-textarea]", "[rst-search]", "[rst-dropdown]", "[rst-switch-track]", "[rst-switch-track]::after", "[rst-switch]:has(input:checked) [rst-switch-track]", "[rst-tblock] > [rst-tblock-head]:has(> input:checked) > [rst-switch-track]", "[rst-seg-tabs] a[aria-current]", `[rst-seg-tabs] > button[aria-pressed="true"]`, "[rst-pagination] [aria-current=\"page\"]", "[rst-dropdown-menu]", "[rst-row-menu-panel]", |
| "[rst-shell-sidebar] [rst-shell-nav] a[aria-current]", "[rst-shell-console] [rst-shell-nav] a[aria-current]", "var(--rst-line-strong)", "background-image: none", "box-shadow: none", "outline: 1px solid var(--rst-line-strong)")}, |
| } { |
| i := strings.LastIndex(css, block.header) |
diff --git a/check/segtabs_browser_test.go b/check/segtabs_browser_test.go| new file mode 100644 |
| index 0000000..e4ae9db |
| --- /dev/null |
| +++ b/check/segtabs_browser_test.go |
| @@ -0,0 +1,154 @@ |
| +//go:build browser |
| + |
| +// A seg-tabs track made of buttons, for a choice that changes the page |
| +// in place: each button must be the tab a link is, or a page that |
| +// mixes the two, or moves from one to the other, shows two controls. |
| +package check |
| + |
| +import ( |
| + "context" |
| + "fmt" |
| + "net/http" |
| + "strings" |
| + "testing" |
| + "time" |
| + |
| + "github.com/chromedp/cdproto/emulation" |
| + "github.com/chromedp/chromedp" |
| + |
| + "amadan.net/rastrillo/fiesta" |
| + "amadan.net/rastrillo/fiesta/internal/harness" |
| +) |
| + |
| +// segTabsTrack is one track holding the current link, a plain link, |
| +// the pressed button and an unpressed one, so each button is read |
| +// beside the link it must look like, under the same rules. |
| +const segTabsTrack = `<main rst-page><div rst-seg-tabs role="group" aria-label="View">` + |
| + `<a id="current" href="#list" aria-current="page">List</a><a id="link" href="#grid">Grid</a>` + |
| + `<button id="pressed" type="button" aria-pressed="true">List</button><button id="unpressed" type="button" aria-pressed="false">Grid</button>` + |
| + `</div></main>` |
| + |
| +// segTabsPages serves the track at /<theme>/<scheme>/ under tokens.css |
| +// and that theme, with no script, so nothing but the stylesheets |
| +// decides what each tab looks like. |
| +func segTabsPages(t *testing.T) http.Handler { |
| + t.Helper() |
| + mux := http.NewServeMux() |
| + mux.HandleFunc("GET /tokens.css", func(w http.ResponseWriter, _ *http.Request) { |
| + w.Header().Set("Content-Type", "text/css") |
| + w.Write(fiesta.TokensCSS()) |
| + }) |
| + mux.HandleFunc("GET /theme/{name}", func(w http.ResponseWriter, r *http.Request) { |
| + css, ok := fiesta.ThemeCSS(strings.TrimSuffix(r.PathValue("name"), ".css")) |
| + if !ok { |
| + http.NotFound(w, r) |
| + return |
| + } |
| + w.Header().Set("Content-Type", "text/css") |
| + w.Write(css) |
| + }) |
| + mux.HandleFunc("GET /{theme}/{scheme}/", func(w http.ResponseWriter, r *http.Request) { |
| + w.Header().Set("Content-Type", "text/html; charset=utf-8") |
| + fmt.Fprintf(w, `<!doctype html><html lang="en" dir="ltr" data-theme=%q><head><meta charset="utf-8">`+ |
| + `<meta name="viewport" content="width=device-width"><title>seg-tabs</title>`+ |
| + `<link rel="stylesheet" href="/tokens.css"><link rel="stylesheet" href="/theme/%s.css">`+ |
| + `</head><body>%s</body></html>`, r.PathValue("scheme"), r.PathValue("theme"), segTabsTrack) |
| + }) |
| + return mux |
| +} |
| + |
| +// segTabFace is what a reader sees of a tab: its fill, its text and |
| +// its shape, the box a finger or a pointer has to hit, and the display |
| +// that centres its label in that box on a phone. |
| +var segTabFace = []string{"background-color", "color", "font-family", "font-size", "font-weight", "line-height", "padding", "border-radius", "box-shadow", "block-size", "min-block-size", "min-inline-size", "display", "align-items", "justify-content", "cursor"} |
| + |
| +// sameFace reports each property of segTabFace in which button differs |
| +// from link. |
| +func sameFace(t *testing.T, ctx context.Context, where, button, link string) { |
| + t.Helper() |
| + for _, prop := range segTabFace { |
| + if b, l := computed(t, ctx, button, prop), computed(t, ctx, link, prop); b != l { |
| + t.Errorf("%s: %s's %s is %q, %s's is %q; a button in a track must be the tab a link is", where, button, prop, b, link, l) |
| + } |
| + } |
| +} |
| + |
| +// TestASegTabButtonLooksLikeASegTabLink: in every theme and scheme, |
| +// with a mouse and on a touch screen, the pressed button is the thumb |
| +// the current link is, and an unpressed button is a plain tab, with |
| +// no border and no fill of the browser's own; hovered, it turns the |
| +// colour a hovered link does. Under forced colours and in print the |
| +// pressed button keeps the current link's system colours and loses |
| +// its lift the same way, or the choice made would vanish there. |
| +func TestASegTabButtonLooksLikeASegTabLink(t *testing.T) { |
| + for _, coarse := range []bool{false, true} { |
| + // A mouse on a desktop meets the desktop floor, a finger on a |
| + // phone the touch floor and its centring: each is a rule the |
| + // buttons must share with the links. |
| + pointer, width := "mouse at 1280", int64(1280) |
| + var opts []harness.Option |
| + if coarse { |
| + pointer, width = "touch at 390", 390 |
| + opts = append(opts, harness.WithCoarsePointer()) |
| + } |
| + rig := harness.New(t, func(string) http.Handler { return segTabsPages(t) }, opts...) |
| + ctx, cancel := context.WithTimeout(rig.Context(), 90*time.Second) |
| + mustRun(t, ctx, chromedp.EmulateViewport(width, 844)) |
| + for _, theme := range fiesta.ThemeNames() { |
| + for _, scheme := range []string{"light", "dark"} { |
| + where := fmt.Sprintf("%s %s, %s", theme, scheme, pointer) |
| + mustRun(t, ctx, chromedp.Navigate(fmt.Sprintf("%s/%s/%s/", rig.Origin, theme, scheme)), chromedp.WaitVisible("#unpressed", chromedp.ByQuery)) |
| + requirePointer(t, ctx, coarse) |
| + still(t, ctx) |
| + sameFace(t, ctx, where, "#pressed", "#current") |
| + sameFace(t, ctx, where, "#unpressed", "#link") |
| + if w := computed(t, ctx, "#unpressed", "border-width"); w != "0px" { |
| + t.Errorf("%s: the unpressed button's border is %s wide; the browser's button edge must go", where, w) |
| + } |
| + if bg := computed(t, ctx, "#unpressed", "background-color"); bg != "rgba(0, 0, 0, 0)" { |
| + t.Errorf("%s: the unpressed button is filled %s; only the pressed one wears the thumb", where, bg) |
| + } |
| + rest := computed(t, ctx, "#link", "color") |
| + forceState(t, ctx, "#link", "hover") |
| + forceState(t, ctx, "#unpressed", "hover") |
| + hovered := computed(t, ctx, "#link", "color") |
| + if hovered == rest { |
| + t.Errorf("%s: CONTROL: a hovered link is %s, the colour it rests in; the hover below cannot be told from rest", where, rest) |
| + } |
| + if b := computed(t, ctx, "#unpressed", "color"); b != hovered { |
| + t.Errorf("%s: a hovered unpressed button is %s, a hovered link %s", where, b, hovered) |
| + } |
| + forceState(t, ctx, "#link") |
| + forceState(t, ctx, "#unpressed") |
| + } |
| + } |
| + cancel() |
| + } |
| + |
| + // Forced colours and print, in Day light, where the axis is |
| + // flattened and the current tab is drawn by rules of its own. |
| + rig := harness.New(t, func(string) http.Handler { return segTabsPages(t) }) |
| + ctx, cancel := context.WithTimeout(rig.Context(), 60*time.Second) |
| + defer cancel() |
| + for _, medium := range []struct { |
| + name, query string |
| + emulate *emulation.SetEmulatedMediaParams |
| + }{ |
| + {"forced colours", "(forced-colors: active)", emulation.SetEmulatedMedia().WithFeatures([]*emulation.MediaFeature{{Name: "forced-colors", Value: "active"}})}, |
| + {"print", "print", emulation.SetEmulatedMedia().WithMedia("print")}, |
| + } { |
| + mustRun(t, ctx, chromedp.EmulateViewport(1280, 800), medium.emulate, chromedp.Navigate(rig.Origin+"/day/light/"), chromedp.WaitVisible("#pressed", chromedp.ByQuery)) |
| + var on bool |
| + at(t, ctx, `JSON.stringify(matchMedia(`+jsStr(medium.query)+`).matches)`, &on) |
| + if !on { |
| + t.Fatalf("%s: matchMedia(%q) is false; this leg is not the medium it claims", medium.name, medium.query) |
| + } |
| + still(t, ctx) |
| + for _, prop := range []string{"background-color", "color", "box-shadow"} { |
| + if b, l := computed(t, ctx, "#pressed", prop), computed(t, ctx, "#current", prop); b != l { |
| + t.Errorf("%s: the pressed button's %s is %q, the current link's %q", medium.name, prop, b, l) |
| + } |
| + } |
| + mustRun(t, ctx, emulation.SetEmulatedMedia()) |
| + } |
| +} |
diff --git a/check/sizing_browser_test.go b/check/sizing_browser_test.go| index aa6745c..366383e 100644 |
| --- a/check/sizing_browser_test.go |
| +++ b/check/sizing_browser_test.go |
| @@ -709,6 +709,7 @@ var fixtureCounts = []struct { |
| {`[data-partial="pagination"] [rst-pagination] a:not([rst-pagination-wide])`, 2}, |
| {`[data-partial="bulk-bar"] [rst-dropdown-menu] button`, 2}, |
| {`[data-partial="seg-tabs"] a`, 2}, |
| + {`[data-partial="seg-tabs"] [rst-seg-tabs] > button`, 2}, |
| {`[data-partial="dropdown"] [rst-dropdown-menu] a`, 1}, |
| {`[data-extra="short-labels"] [rst-bulkbar-escalate]`, 1}, |
| {`[data-extra="short-labels"] a[rst-person]`, 1}, |
diff --git a/check/sizing_test.go b/check/sizing_test.go| index d4d9314..b0055b0 100644 |
| --- a/check/sizing_test.go |
| +++ b/check/sizing_test.go |
| @@ -344,6 +344,7 @@ var tapInventory = []tapEntry{ |
| {"[rst-person]", "Standalone person link", "rst-person", "a[rst-person]"}, |
| {"[rst-pagination] > a", "Pagination chips", "rst-pagination", "[rst-pagination] > a"}, |
| {"[rst-seg-tabs] a", "Segmented tabs", "rst-seg-tabs", "[rst-seg-tabs] a"}, |
| + {"[rst-seg-tabs] > button", "Segmented tabs: buttons", "rst-seg-tabs", "[rst-seg-tabs] > button"}, |
| {"[rst-switch]", "Switch", "rst-switch", "[rst-switch] input"}, |
| {"[rst-choice-cards] label", "Choice cards", "rst-choice-cards", "[rst-choice-cards] input"}, |
| {"[rst-tblock-head]", "Toggle-block head", "rst-tblock-head", "[rst-tblock-head] input"}, |
diff --git a/check/testdata/fixtures/sizing.html b/check/testdata/fixtures/sizing.html| index 483cbc3..39fe594 100644 |
| --- a/check/testdata/fixtures/sizing.html |
| +++ b/check/testdata/fixtures/sizing.html |
| @@ -168,7 +168,7 @@ |
| </details></div><div rst-page data-partial="form-foot"><div rst-form-foot> |
| <button rst-btn="primary lg" type="submit">Save</button> |
| <a rst-btn="lg" href="/admin/posts">Back to posts</a> |
| -</div></div><div rst-page data-partial="field-check"><label rst-switch><input type="checkbox" name="notify" checked><span rst-switch-track aria-hidden="true"></span> Email me about replies</label></div><div rst-page data-partial="choice-field"><fieldset rst-choice><legend>Plan</legend><div rst-choice-cards><label><input type="radio" name="plan" value="free"><span><span rst-choice-title>Free</span><span rst-choice-desc>Good to start.</span></span></label><label><input type="radio" name="plan" value="pro" checked><span><span rst-choice-title>Pro</span><span rst-choice-desc>For growing teams.</span></span></label></div></fieldset></div><div rst-page data-partial="seg-tabs"><nav rst-seg-tabs aria-label="Sections"><a href="?tab=basics" aria-current="page">Basics</a><a href="?tab=advanced">Advanced</a></nav></div><div rst-page data-partial="confirm-form"><form rst-form-actions method="post" action="/orders/1/refund"><input type="hidden" name="csrf" value="tok"><input type="hidden" name="id" value="42"><a rst-btn="ghost lg" href="/orders/1">Cancel</a><button type="submit" rst-btn="danger lg">Refund €10.00</button></form></div><div rst-page data-partial="back-nav"><nav rst-back-nav><a href="/orders/1">← Order AB3PX</a></nav></div><div rst-page data-partial="bulk-bar"><div rst-bulkbar><a rst-bulkbar-close href="/orders" aria-label="Done selecting"><svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg></a><span rst-bulkbar-selection><span rst-bulkbar-count>3 selected</span><a rst-bulkbar-escalate href="/orders?select=all">Select all 412 matching</a></span><details rst-dropdown name="rst-menus"><summary>Actions<span rst-caret aria-hidden="true"><svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6"/></svg></span></summary><div rst-dropdown-menu><button type="submit" name="action" value="export">Export</button><button type="submit" name="action" value="refund" class="rst-danger">Refund…</button></div></details></div></div><div rst-page data-partial="media-grid"><div rst-media> |
| +</div></div><div rst-page data-partial="field-check"><label rst-switch><input type="checkbox" name="notify" checked><span rst-switch-track aria-hidden="true"></span> Email me about replies</label></div><div rst-page data-partial="choice-field"><fieldset rst-choice><legend>Plan</legend><div rst-choice-cards><label><input type="radio" name="plan" value="free"><span><span rst-choice-title>Free</span><span rst-choice-desc>Good to start.</span></span></label><label><input type="radio" name="plan" value="pro" checked><span><span rst-choice-title>Pro</span><span rst-choice-desc>For growing teams.</span></span></label></div></fieldset></div><div rst-page data-partial="seg-tabs"><nav rst-seg-tabs aria-label="Sections"><a href="?tab=basics" aria-current="page">Basics</a><a href="?tab=advanced">Advanced</a></nav><div rst-seg-tabs role="group" aria-label="View"><button type="button" aria-pressed="true">List</button><button type="button" aria-pressed="false">Grid</button></div></div><div rst-page data-partial="confirm-form"><form rst-form-actions method="post" action="/orders/1/refund"><input type="hidden" name="csrf" value="tok"><input type="hidden" name="id" value="42"><a rst-btn="ghost lg" href="/orders/1">Cancel</a><button type="submit" rst-btn="danger lg">Refund €10.00</button></form></div><div rst-page data-partial="back-nav"><nav rst-back-nav><a href="/orders/1">← Order AB3PX</a></nav></div><div rst-page data-partial="bulk-bar"><div rst-bulkbar><a rst-bulkbar-close href="/orders" aria-label="Done selecting"><svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg></a><span rst-bulkbar-selection><span rst-bulkbar-count>3 selected</span><a rst-bulkbar-escalate href="/orders?select=all">Select all 412 matching</a></span><details rst-dropdown name="rst-menus"><summary>Actions<span rst-caret aria-hidden="true"><svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6"/></svg></span></summary><div rst-dropdown-menu><button type="submit" name="action" value="export">Export</button><button type="submit" name="action" value="refund" class="rst-danger">Refund…</button></div></details></div></div><div rst-page data-partial="media-grid"><div rst-media> |
| <div rst-media-tile> |
| <div rst-media-frame> |
| <img src="/images/4471.png" alt="" loading="lazy" decoding="async" width="1280" height="3200"> |
diff --git a/check/testdata/fixtures/spelling-class.html b/check/testdata/fixtures/spelling-class.html| index 4f21350..cb68515 100644 |
| --- a/check/testdata/fixtures/spelling-class.html |
| +++ b/check/testdata/fixtures/spelling-class.html |
| @@ -60,6 +60,7 @@ |
| <nav class="rst-pagination" aria-label="Pagination"><a href="#a">Previous</a><span class="rst-pagination__disabled" >First</span><span class="rst-pagination__gap" >…</span><span class="rst-pagination__gap rst-pagination__gap--narrow" aria-hidden="true">…</span><a href="#a" aria-current="page">2</a><a href="#a">Next</a></nav> |
| <div class="rst-seg-tabs" ><a href="#a" aria-current="page">All</a><a href="#a">Open</a><a href="#a">Closed</a></div> |
| + <div class="rst-seg-tabs" role="group" aria-label="View"><button type="button" aria-pressed="true">List</button><button type="button" aria-pressed="false">Grid</button></div> |
| <div class="rst-meter" ><meter class="rst-meter__bar" value="40" min="0" max="100" aria-hidden="true"></meter><span class="rst-meter__num" >40%</span></div> |
| <div class="rst-job" ><span class="rst-spin" aria-hidden="true"></span> <strong>Import</strong> is running — 128 of 400…<progress class="rst-job__bar" value="32" min="0" max="100" aria-hidden="true"></progress></div> |
diff --git a/css/tokens.css b/css/tokens.css| index 34ba896..35002fd 100644 |
| --- a/css/tokens.css |
| +++ b/css/tokens.css |
| @@ -235,7 +235,7 @@ body { |
| component that sizes its icon (the 12px caret, .rst-caret) still |
| wins. Not the list grid's title: it is the row's name, text that an |
| icon should follow, and the row is its target. */ |
| -:where(.rst-btn, .rst-search, .rst-search__clear, .rst-ftok a, .rst-help, .rst-dropdown > summary, .rst-dropdown__menu a, .rst-dropdown__menu button, .rst-dropdown__menu .rst-menu-group > summary, .rst-row-menu__panel a, .rst-row-menu__panel button, .rst-locale button, .rst-scheme button, .rst-combo__option, .rst-dtp__row, .rst-dtp__pick, .rst-cal__nav, .rst-row__action, .rst-row-menu > summary, .rst-pagination > a, .rst-seg-tabs a, .rst-switch, .rst-choice__cards label, .rst-tblock__head, .rst-bulkbar__close, .rst-bulkbar__escalate, .rst-modal-close, .rst-modal-panel > nav a, .rst-back-nav a, .rst-signin__providers > a, .rst-shell__brand, .rst-shell__nav a, .rst-shell__menu > summary, .rst-shell__chrome > summary, .rst-shell__account > summary, .rst-shell__back > a) .icon, :where([rst-btn], [rst-search], [rst-search-clear], [rst-ftok] a, [rst-help], [rst-dropdown] > summary, [rst-dropdown-menu] a, [rst-dropdown-menu] button, [rst-dropdown-menu] [rst-menu-group] > summary, [rst-row-menu-panel] a, [rst-row-menu-panel] button, [rst-locale] button, [rst-scheme] button, [rst-combo-option], [rst-dtp-row], [rst-dtp-pick], [rst-cal-nav], [rst-row-action], [rst-row-menu] > summary, [rst-pagination] > a, [rst-seg-tabs] a, [rst-switch], [rst-choice-cards] label, [rst-tblock-head], [rst-bulkbar-close], [rst-bulkbar-escalate], [rst-modal-close], [rst-modal-panel] > nav a, [rst-back-nav] a, [rst-signin-providers] > a, [rst-shell-brand], [rst-shell-nav] a, [rst-shell-menu] > summary, [rst-shell-chrome] > summary, [rst-shell-account] > summary, [rst-shell-back] > a) .icon { |
| +:where(.rst-btn, .rst-search, .rst-search__clear, .rst-ftok a, .rst-help, .rst-dropdown > summary, .rst-dropdown__menu a, .rst-dropdown__menu button, .rst-dropdown__menu .rst-menu-group > summary, .rst-row-menu__panel a, .rst-row-menu__panel button, .rst-locale button, .rst-scheme button, .rst-combo__option, .rst-dtp__row, .rst-dtp__pick, .rst-cal__nav, .rst-row__action, .rst-row-menu > summary, .rst-pagination > a, .rst-seg-tabs a, .rst-seg-tabs > button, .rst-switch, .rst-choice__cards label, .rst-tblock__head, .rst-bulkbar__close, .rst-bulkbar__escalate, .rst-modal-close, .rst-modal-panel > nav a, .rst-back-nav a, .rst-signin__providers > a, .rst-shell__brand, .rst-shell__nav a, .rst-shell__menu > summary, .rst-shell__chrome > summary, .rst-shell__account > summary, .rst-shell__back > a) .icon, :where([rst-btn], [rst-search], [rst-search-clear], [rst-ftok] a, [rst-help], [rst-dropdown] > summary, [rst-dropdown-menu] a, [rst-dropdown-menu] button, [rst-dropdown-menu] [rst-menu-group] > summary, [rst-row-menu-panel] a, [rst-row-menu-panel] button, [rst-locale] button, [rst-scheme] button, [rst-combo-option], [rst-dtp-row], [rst-dtp-pick], [rst-cal-nav], [rst-row-action], [rst-row-menu] > summary, [rst-pagination] > a, [rst-seg-tabs] a, [rst-seg-tabs] > button, [rst-switch], [rst-choice-cards] label, [rst-tblock-head], [rst-bulkbar-close], [rst-bulkbar-escalate], [rst-modal-close], [rst-modal-panel] > nav a, [rst-back-nav] a, [rst-signin-providers] > a, [rst-shell-brand], [rst-shell-nav] a, [rst-shell-menu] > summary, [rst-shell-chrome] > summary, [rst-shell-account] > summary, [rst-shell-back] > a) .icon { |
| block-size: 1rem; |
| inline-size: 1rem; |
| } |
| @@ -2132,11 +2132,21 @@ small.rst-field__hint, small[rst-field-hint] { display: block; font-size: var(-- |
| or grid parent that lays the tabs out in a row of controls spaces its |
| own items and should zero it. */ |
| .rst-seg-tabs, [rst-seg-tabs] { background: var(--rst-accent-soft); border-radius: 8px; display: flex; flex-wrap: wrap; gap: 2px; margin-block: 0 var(--rst-sp-4); max-inline-size: 100%; padding: 2px; width: max-content; } |
| -.rst-seg-tabs a, [rst-seg-tabs] a { border-radius: var(--rst-radius-sm); color: var(--rst-text-muted); font-size: var(--rst-fs-sm); font-weight: 550; padding: 5px 16px; text-decoration: none; -webkit-tap-highlight-color: transparent; } |
| -.rst-seg-tabs a:hover, [rst-seg-tabs] a:hover { color: var(--rst-text); } |
| +/* A track of buttons is for a choice that changes the page in place |
| + (a view), its script moving aria-pressed; links stay right where the |
| + choice has an address, since they work with no script. Each button |
| + is the track's own child, so a button nested in a tab's content is |
| + left alone, and it wears exactly what a link wears: this reset takes |
| + away the browser's face, which would otherwise show grey, bordered |
| + and in the system font beside the links. */ |
| +.rst-seg-tabs > button, [rst-seg-tabs] > button { background: none; border: 0; cursor: pointer; font-family: inherit; line-height: inherit; margin: 0; } |
| +.rst-seg-tabs a, [rst-seg-tabs] a, .rst-seg-tabs > button, [rst-seg-tabs] > button { border-radius: var(--rst-radius-sm); color: var(--rst-text-muted); font-size: var(--rst-fs-sm); font-weight: 550; padding: 5px 16px; text-decoration: none; -webkit-tap-highlight-color: transparent; } |
| +.rst-seg-tabs a:hover, [rst-seg-tabs] a:hover, .rst-seg-tabs > button:hover, [rst-seg-tabs] > button:hover { color: var(--rst-text); } |
| /* The current tab is a thumb: a top highlight first, then the lift. |
| + The pressed button is the same thumb, so a reader sees one choice |
| + made whichever element made it. |
| No press rule, because a pressed tab has no fill today (A15). */ |
| -.rst-seg-tabs a[aria-current], [rst-seg-tabs] a[aria-current] { background: var(--rst-surface); box-shadow: inset var(--rst-hl-x) 1px 0 var(--rst-depth-hl, transparent), var(--rst-shadow-lift); color: var(--rst-text); font-weight: 600; } |
| +.rst-seg-tabs a[aria-current], [rst-seg-tabs] a[aria-current], .rst-seg-tabs > button[aria-pressed="true"], [rst-seg-tabs] > button[aria-pressed="true"] { background: var(--rst-surface); box-shadow: inset var(--rst-hl-x) 1px 0 var(--rst-depth-hl, transparent), var(--rst-shadow-lift); color: var(--rst-text); font-weight: 600; } |
| /* form layout — the interview rhythm and the save bar. */ |
| .rst-form-flow > .rst-field + .rst-field, [rst-form-flow] > [rst-field] + [rst-field] { margin-top: var(--rst-sp-5); } |
| @@ -3415,7 +3425,7 @@ dialog.rst-modal-panel, dialog[rst-modal-panel] { block-size: auto; color: inher |
| .rst-btn, [rst-btn] { justify-content: center; } |
| .rst-dropdown__menu .rst-menu-group > summary, [rst-dropdown-menu] [rst-menu-group] > summary, .rst-dropdown__menu a, [rst-dropdown-menu] a, .rst-dropdown__menu button, [rst-dropdown-menu] button, .rst-row-menu__panel a, [rst-row-menu-panel] a, .rst-row-menu__panel button, [rst-row-menu-panel] button, .rst-locale button, .rst-scheme button, [rst-locale] button, [rst-scheme] button, .rst-combo__option, [rst-combo-option], .rst-dtp__row, [rst-dtp-row], .rst-modal-panel > nav a, [rst-modal-panel] > nav a { align-items: center; display: flex; } |
| .rst-shell-topbar .rst-shell__nav a, [rst-shell-topbar] [rst-shell-nav] a, .rst-shell-sidebar .rst-shell__nav a, [rst-shell-sidebar] [rst-shell-nav] a, .rst-shell-console .rst-shell__nav a, [rst-shell-console] [rst-shell-nav] a { align-items: center; display: flex; } |
| - .rst-pagination > a, [rst-pagination] > a, .rst-pagination > span, [rst-pagination] > span, .rst-seg-tabs a, [rst-seg-tabs] a { align-items: center; display: inline-flex; justify-content: center; } |
| + .rst-pagination > a, [rst-pagination] > a, .rst-pagination > span, [rst-pagination] > span, .rst-seg-tabs a, [rst-seg-tabs] a, .rst-seg-tabs > button, [rst-seg-tabs] > button { align-items: center; display: inline-flex; justify-content: center; } |
| /* A phone's chip is the 44px tap square it has always been, narrower |
| than the desktop's own minimum above. */ |
| .rst-pagination > a, [rst-pagination] > a, .rst-pagination > span, [rst-pagination] > span { min-inline-size: var(--rst-target); } |
| @@ -3522,7 +3532,7 @@ dialog.rst-modal-panel, dialog[rst-modal-panel] { block-size: auto; color: inher |
| .rst-switch__track::after, [rst-switch-track]::after { background: ButtonText; } |
| .rst-switch:has(input:checked) .rst-switch__track, [rst-switch]:has(input:checked) [rst-switch-track], .rst-tblock > .rst-tblock__head:has(> input:checked) > .rst-switch__track, [rst-tblock] > [rst-tblock-head]:has(> input:checked) > [rst-switch-track] { background: Highlight; } |
| .rst-switch:has(input:checked) .rst-switch__track::after, [rst-switch]:has(input:checked) [rst-switch-track]::after, .rst-tblock > .rst-tblock__head:has(> input:checked) > .rst-switch__track::after, [rst-tblock] > [rst-tblock-head]:has(> input:checked) > [rst-switch-track]::after { background: HighlightText; } |
| - .rst-shell-sidebar .rst-shell__nav a[aria-current], [rst-shell-sidebar] [rst-shell-nav] a[aria-current], .rst-shell-console .rst-shell__nav a[aria-current], [rst-shell-console] [rst-shell-nav] a[aria-current], .rst-shell-topbar .rst-shell__tail > .rst-shell__nav a[aria-current], [rst-shell-topbar] [rst-shell-tail] > [rst-shell-nav] a[aria-current], .rst-dropdown__menu a[aria-current], [rst-dropdown-menu] a[aria-current], .rst-pagination [aria-current="page"], [rst-pagination] [aria-current="page"], .rst-seg-tabs a[aria-current], [rst-seg-tabs] a[aria-current] { background: Highlight; box-shadow: none; color: HighlightText; } |
| + .rst-shell-sidebar .rst-shell__nav a[aria-current], [rst-shell-sidebar] [rst-shell-nav] a[aria-current], .rst-shell-console .rst-shell__nav a[aria-current], [rst-shell-console] [rst-shell-nav] a[aria-current], .rst-shell-topbar .rst-shell__tail > .rst-shell__nav a[aria-current], [rst-shell-topbar] [rst-shell-tail] > [rst-shell-nav] a[aria-current], .rst-dropdown__menu a[aria-current], [rst-dropdown-menu] a[aria-current], .rst-pagination [aria-current="page"], [rst-pagination] [aria-current="page"], .rst-seg-tabs a[aria-current], [rst-seg-tabs] a[aria-current], .rst-seg-tabs > button[aria-pressed="true"], [rst-seg-tabs] > button[aria-pressed="true"] { background: Highlight; box-shadow: none; color: HighlightText; } |
| .rst-shell-sidebar.rst-shell-sidebar--index .rst-shell__nav > a[aria-current]:hover, .rst-shell-sidebar.rst-shell-sidebar--index .rst-shell__nav > a[aria-current]:active, [rst-shell-sidebar][rst-shell-sidebar~="index"] [rst-shell-nav] > a[aria-current]:hover, [rst-shell-sidebar][rst-shell-sidebar~="index"] [rst-shell-nav] > a[aria-current]:active, .rst-shell-console.rst-shell-console--index .rst-shell__nav > a[aria-current]:hover, .rst-shell-console.rst-shell-console--index .rst-shell__nav > a[aria-current]:active, [rst-shell-console][rst-shell-console~="index"] [rst-shell-nav] > a[aria-current]:hover, [rst-shell-console][rst-shell-console~="index"] [rst-shell-nav] > a[aria-current]:active { background: Highlight; color: HighlightText; } |
| } |
| /* Print drops every shadow, gradient, sheen and tint the same way, |
| @@ -3563,7 +3573,7 @@ dialog.rst-modal-panel, dialog[rst-modal-panel] { block-size: auto; color: inher |
| .rst-switch__track, [rst-switch-track] { border: 1px solid var(--rst-line-strong); box-shadow: none; } |
| .rst-switch:has(input:checked) .rst-switch__track, [rst-switch]:has(input:checked) [rst-switch-track], .rst-tblock > .rst-tblock__head:has(> input:checked) > .rst-switch__track, [rst-tblock] > [rst-tblock-head]:has(> input:checked) > [rst-switch-track] { background-color: var(--rst-line-strong); } |
| .rst-switch__track::after, [rst-switch-track]::after { background-image: none; box-shadow: none; outline: 1px solid var(--rst-line-strong); outline-offset: -1px; } |
| - .rst-shell-sidebar .rst-shell__nav a[aria-current], [rst-shell-sidebar] [rst-shell-nav] a[aria-current], .rst-shell-console .rst-shell__nav a[aria-current], [rst-shell-console] [rst-shell-nav] a[aria-current], .rst-shell-topbar .rst-shell__tail > .rst-shell__nav a[aria-current], [rst-shell-topbar] [rst-shell-tail] > [rst-shell-nav] a[aria-current], .rst-seg-tabs a[aria-current], [rst-seg-tabs] a[aria-current], .rst-pagination [aria-current="page"], [rst-pagination] [aria-current="page"] { box-shadow: none; } |
| + .rst-shell-sidebar .rst-shell__nav a[aria-current], [rst-shell-sidebar] [rst-shell-nav] a[aria-current], .rst-shell-console .rst-shell__nav a[aria-current], [rst-shell-console] [rst-shell-nav] a[aria-current], .rst-shell-topbar .rst-shell__tail > .rst-shell__nav a[aria-current], [rst-shell-topbar] [rst-shell-tail] > [rst-shell-nav] a[aria-current], .rst-seg-tabs a[aria-current], [rst-seg-tabs] a[aria-current], .rst-seg-tabs > button[aria-pressed="true"], [rst-seg-tabs] > button[aria-pressed="true"], .rst-pagination [aria-current="page"], [rst-pagination] [aria-current="page"] { box-shadow: none; } |
| /* The current chip's edge was its fill, transparent above, and the |
| other chips keep their line: without this the current page is the |
| one chip with no outline on paper. */ |
| @@ -3665,7 +3675,7 @@ a.rst-person, a[rst-person] { box-sizing: border-box; min-block-size: var(--rst- |
| /* The chip's own minimum width is over the floor on a desktop; the |
| touch query sets the phone's. */ |
| .rst-pagination > a, [rst-pagination] > a, .rst-pagination > span, [rst-pagination] > span { align-content: center; box-sizing: border-box; min-block-size: var(--rst-target); } |
| -.rst-seg-tabs a, [rst-seg-tabs] a { align-content: center; box-sizing: border-box; min-block-size: var(--rst-target); min-inline-size: var(--rst-target); } |
| +.rst-seg-tabs a, [rst-seg-tabs] a, .rst-seg-tabs > button, [rst-seg-tabs] > button { align-content: center; box-sizing: border-box; min-block-size: var(--rst-target); min-inline-size: var(--rst-target); } |
| .rst-switch, [rst-switch], .rst-choice__cards label, [rst-choice-cards] label, .rst-tblock > .rst-tblock__head, [rst-tblock] > [rst-tblock-head] { box-sizing: border-box; min-block-size: var(--rst-target); } |
| /* flex: none because the bar is a flex row that, at 390, shrank the |
diff --git a/examples/examples.json b/examples/examples.json| index d4fbbbb..7dab11c 100644 |
| --- a/examples/examples.json |
| +++ b/examples/examples.json |
| @@ -184,6 +184,11 @@ |
| "?tab=advanced", |
| "?tab=basics" |
| ] |
| + }, |
| + { |
| + "id": "buttons-that-switch-in-place", |
| + "label": "Buttons that switch in place", |
| + "sampleURLs": [] |
| } |
| ] |
| }, |
diff --git a/examples/prose.json b/examples/prose.json| index 7ddf345..8411a0a 100644 |
| --- a/examples/prose.json |
| +++ b/examples/prose.json |
| @@ -309,7 +309,10 @@ |
| } |
| }, |
| "seg-tabs": { |
| - "blurb": "A segmented control of plain links. The current tab is aria-current on a link." |
| + "blurb": "A segmented control of links or buttons. The current link is aria-current; the pressed button is aria-pressed=\"true\".", |
| + "states": { |
| + "buttons-that-switch-in-place": "Buttons, for a choice that changes the page in place, like a view. Your script moves aria-pressed to the button pressed." |
| + } |
| }, |
| "selbox": { |
| "blurb": "The selection checkbox a list row wears in select mode. Its label restates the row's identity." |
diff --git a/examples/seg-tabs/buttons-that-switch-in-place.html b/examples/seg-tabs/buttons-that-switch-in-place.html| new file mode 100644 |
| index 0000000..886d071 |
| --- /dev/null |
| +++ b/examples/seg-tabs/buttons-that-switch-in-place.html |
| @@ -0,0 +1,13 @@ |
| +<!doctype html> |
| +<html lang="en" dir="ltr"> |
| +<head> |
| +<meta charset="utf-8"> |
| +<meta name="viewport" content="width=device-width"> |
| +<title>seg-tabs: Buttons that switch in place</title> |
| +<link rel="stylesheet" href="../../css/tokens.css"> |
| +<link rel="stylesheet" href="../../css/themes/day.css"> |
| +</head> |
| +<body> |
| +<div rst-seg-tabs role="group" aria-label="View"><button type="button" aria-pressed="true">List</button><button type="button" aria-pressed="false">Grid</button></div> |
| +</body> |
| +</html> |
diff --git a/hack/test-weights.txt b/hack/test-weights.txt| index 7be8d70..b1d6009 100644 |
| --- a/hack/test-weights.txt |
| +++ b/hack/test-weights.txt |
| @@ -93,6 +93,7 @@ browser check TestAPITokensFactsStayVisibleOnAPhone 2 |
| browser check TestAResetInTheTickAfterABlurKeepsTheDefault 2 |
| browser check TestARowsKebabSitsAtItsTrailingEdge 2 |
| browser check TestASchemeFlipLandsWhileHovered 2 |
| +browser check TestASegTabButtonLooksLikeASegTabLink 2 |
| browser check TestAToggleDuringASettleTakesItsOwnCurve 2 |
| browser check TestControlsInARowKeepTheirOwnBoxes 2 |
| browser check TestDaysDerivedTokensReproduceTheirDocumentedColours 2 |
diff --git a/internal/gallery/heights.go b/internal/gallery/heights.go| index bf4de9f..cb4cf1a 100644 |
| --- a/internal/gallery/heights.go |
| +++ b/internal/gallery/heights.go |
| @@ -167,6 +167,7 @@ var previewHeights = map[string]int{ |
| "row-menu/links-only-for-a-list-inside-a-selection-form": 180, |
| "scheme-menu/standalone": 200, |
| "scheme-menu/three-choices-dark-current": 200, |
| + "seg-tabs/buttons-that-switch-in-place": 110, |
| "seg-tabs/two-sections": 110, |
| "selbox/default": 90, |
| "shell-sidebar/default": 260, |
| @@ -337,6 +338,7 @@ var previewMobileLocaleHeights = map[string]map[string]int{ |
| "layout-topbar/default": 1140, |
| "list-row-action/lead-marker-status-and-an-action": 290, |
| "modal/default": 350, |
| + "seg-tabs/buttons-that-switch-in-place": 170, |
| "seg-tabs/two-sections": 170, |
| "signin/after-the-link-is-sent": 640, |
| "signin/after-the-link-is-sent-in-another-browser": 640, |
diff --git a/internal/gallery/i18n.go b/internal/gallery/i18n.go| index be539dc..be466a0 100644 |
| --- a/internal/gallery/i18n.go |
| +++ b/internal/gallery/i18n.go |
| @@ -8,7 +8,6 @@ import ( |
| "io/fs" |
| "regexp" |
| "slices" |
| - "strings" |
| "sync" |
| "amadan.net/rastrillo/fiesta/example" |
| @@ -171,7 +170,7 @@ func EnglishStrings(src fs.FS) (map[string]string, error) { |
| } |
| for _, sm := range samples { |
| for _, key := range sampleKeys(sm[0], sm[1]) { |
| - out[sampleID(key)] = strings.TrimSuffix(key, verbSense) |
| + out[sampleID(key)] = withoutSense(key) |
| } |
| } |
| return out, nil |
diff --git a/internal/gallery/i18n/ar.json b/internal/gallery/i18n/ar.json| index cfea78d..14b36de 100644 |
| --- a/internal/gallery/i18n/ar.json |
| +++ b/internal/gallery/i18n/ar.json |
| @@ -95,7 +95,8 @@ |
| "examples.scheme-menu.blurb": "مبدّل نظام الألوان: قائمة details من نماذج POST ذات حقل واحد لـ\"فاتح\" و\"داكن\" و\"حسب النظام\". حين يُرسَل الاختيار يذهب إلى تطبيقك؛ ومستقلًا يتذكّره fiesta.js في المتصفح.", |
| "examples.scheme-menu.states.standalone": "لا تطبيق وراءها: النماذج بلا action، ومع data-rst-scheme-store=\"local\" على عنصر <html> يتذكّر fiesta.js الاختيار في المتصفح. وهذه هي التي يستخدمها رأس المعرض نفسه.", |
| "examples.scheme-menu.states.three-choices-dark-current": "كل نظام نموذج بحقل واحد، فاختيار أحدها طلب POST يجيب عنه تطبيقك، وتعود الصفحة والنظام المختار معلَّم بأنه الحالي.", |
| - "examples.seg-tabs.blurb": "عنصر تحكم مقسّم من روابط عادية. علامة التبويب الحالية هي aria-current على رابط.", |
| + "examples.seg-tabs.blurb": "عنصر تحكم مقسّم من روابط أو أزرار. الرابط الحالي هو aria-current، والزر المضغوط هو aria-pressed=\"true\".", |
| + "examples.seg-tabs.states.buttons-that-switch-in-place": "أزرار، لخيار يغيّر الصفحة في مكانها، مثل طريقة العرض. ينقل سكربتك aria-pressed إلى الزر المضغوط.", |
| "examples.selbox.blurb": "مربّع التأشير الذي يرتديه صف القائمة في وضع التحديد. وتسميته تعيد ذكر هويّة الصف.", |
| "examples.shell-sidebar.blurb": "إطار الهيكل sidebar في صفحة محتوى: على الهاتف، عنصر للرجوع إلى الفهرس.", |
| "examples.shell-topbar.blurb": "إطار الهيكل ذي الشريط العلوي نفسه، كترميز. والصفحة الكاملة التي ينتمي إليها هي layout-topbar، تحت الهياكل.", |
| @@ -369,6 +370,7 @@ |
| "labels.row-menu.links-only-for-a-list-inside-a-selection-form": "روابط فقط، لقائمة داخل نموذج تحديد", |
| "labels.scheme-menu.standalone": "مستقل: الصفحة تتذكّر الاختيار", |
| "labels.scheme-menu.three-choices-dark-current": "ثلاثة خيارات، والداكن هو الحالي.", |
| + "labels.seg-tabs.buttons-that-switch-in-place": "أزرار تبدّل في مكانها", |
| "labels.seg-tabs.two-sections": "قسمان", |
| "labels.signin.a-passkey": "مفتاح مرور", |
| "labels.signin.after-the-link-is-sent": "بعد إرسال الرابط", |
diff --git a/internal/gallery/i18n/bn.json b/internal/gallery/i18n/bn.json| index 389876c..2e06c19 100644 |
| --- a/internal/gallery/i18n/bn.json |
| +++ b/internal/gallery/i18n/bn.json |
| @@ -95,7 +95,8 @@ |
| "examples.scheme-menu.blurb": "রঙের ধরন বদলানোর সুইচ: \"হালকা\", \"গাঢ়\" আর \"সিস্টেম\"-এর জন্য এক-ফিল্ডের POST ফর্মের একটি details মেনু। পাঠানো হলে পছন্দটি আপনার অ্যাপে যায়; একা থাকলে fiesta.js সেটি ব্রাউজারে মনে রাখে।", |
| "examples.scheme-menu.states.standalone": "পেছনে কোনো অ্যাপ নেই: ফর্মগুলোর কোনো action নেই, আর <html> উপাদানে data-rst-scheme-store=\"local\" থাকলে fiesta.js পছন্দটি ব্রাউজারে মনে রাখে। গ্যালারির নিজের হেডার এটিই ব্যবহার করে।", |
| "examples.scheme-menu.states.three-choices-dark-current": "প্রতিটি ধরন এক-ফিল্ডের একটি ফর্ম, তাই একটি বেছে নেওয়া মানে একটি POST, যার জবাব দেয় আপনার অ্যাপ, আর পাতাটি ফিরে আসে বেছে নেওয়া ধরনটিকে বর্তমান হিসেবে চিহ্নিত করে।", |
| - "examples.seg-tabs.blurb": "সাধারণ লিঙ্ক দিয়ে তৈরি একটি ভাগ করা নিয়ন্ত্রণ। বর্তমান ট্যাবটি হলো একটি লিঙ্কে aria-current।", |
| + "examples.seg-tabs.blurb": "লিঙ্ক বা বোতাম দিয়ে তৈরি একটি ভাগ করা নিয়ন্ত্রণ। বর্তমান লিঙ্কটি হলো aria-current; চাপা বোতামটি হলো aria-pressed=\"true\"।", |
| + "examples.seg-tabs.states.buttons-that-switch-in-place": "বোতাম, এমন পছন্দের জন্য যা পাতাটিকে জায়গাতেই বদলে দেয়, যেমন একটি ভিউ। আপনার স্ক্রিপ্ট aria-pressed চাপা বোতামে সরিয়ে নেয়।", |
| "examples.selbox.blurb": "নির্বাচন মোডে তালিকার সারি যে চেকবক্সটি পরে। এর লেবেল সারিটির পরিচয় আবার বলে দেয়।", |
| "examples.shell-sidebar.blurb": "কোনো কনটেন্ট পাতায় sidebar শেলের কাঠামো: ফোনে, সূচিতে ফিরে যাওয়ার একটি নিয়ন্ত্রণ।", |
| "examples.shell-topbar.blurb": "টপবার শেলের নিজের কাঠামো, মার্কআপ হিসেবে। যে পুরো পাতার অংশ এটি, সেটি layout-topbar, শেলের নিচে।", |
| @@ -369,6 +370,7 @@ |
| "labels.row-menu.links-only-for-a-list-inside-a-selection-form": "শুধু লিংক, একটি নির্বাচন ফর্মের ভেতরের তালিকার জন্য", |
| "labels.scheme-menu.standalone": "একা: পাতা পছন্দটি মনে রাখে", |
| "labels.scheme-menu.three-choices-dark-current": "তিনটি পছন্দ, বর্তমানে গাঢ়।", |
| + "labels.seg-tabs.buttons-that-switch-in-place": "জায়গাতেই বদলানো বোতাম", |
| "labels.seg-tabs.two-sections": "দুটি বিভাগ", |
| "labels.signin.a-passkey": "একটি পাসকি", |
| "labels.signin.after-the-link-is-sent": "লিঙ্ক পাঠানোর পরে", |
diff --git a/internal/gallery/i18n/drafts.json b/internal/gallery/i18n/drafts.json| index 53512ca..6b25f6c 100644 |
| --- a/internal/gallery/i18n/drafts.json |
| +++ b/internal/gallery/i18n/drafts.json |
| @@ -688,7 +688,23 @@ |
| ] |
| }, |
| "examples.seg-tabs.blurb": { |
| - "english": "A segmented control of plain links. The current tab is aria-current on a link.", |
| + "english": "A segmented control of links or buttons. The current link is aria-current; the pressed button is aria-pressed=\"true\".", |
| + "locales": [ |
| + "ar", |
| + "bn", |
| + "es", |
| + "ga", |
| + "hi", |
| + "ja", |
| + "pt-PT", |
| + "ru", |
| + "vi", |
| + "yue", |
| + "zh-Hans" |
| + ] |
| + }, |
| + "examples.seg-tabs.states.buttons-that-switch-in-place": { |
| + "english": "Buttons, for a choice that changes the page in place, like a view. Your script moves aria-pressed to the button pressed.", |
| "locales": [ |
| "ar", |
| "bn", |
| @@ -1535,6 +1551,22 @@ |
| "zh-Hans" |
| ] |
| }, |
| + "labels.seg-tabs.buttons-that-switch-in-place": { |
| + "english": "Buttons that switch in place", |
| + "locales": [ |
| + "ar", |
| + "bn", |
| + "es", |
| + "ga", |
| + "hi", |
| + "ja", |
| + "pt-PT", |
| + "ru", |
| + "vi", |
| + "yue", |
| + "zh-Hans" |
| + ] |
| + }, |
| "labels.status-pill.negative": { |
| "english": "negative", |
| "locales": [ |
| @@ -1919,6 +1951,22 @@ |
| "zh-Hans" |
| ] |
| }, |
| + "samples.Grid": { |
| + "english": "Grid", |
| + "locales": [ |
| + "ar", |
| + "bn", |
| + "es", |
| + "ga", |
| + "hi", |
| + "ja", |
| + "pt-PT", |
| + "ru", |
| + "vi", |
| + "yue", |
| + "zh-Hans" |
| + ] |
| + }, |
| "samples.Invoice #4471 never arrived<small><bdi>Fiona Reid</bdi> · 09:12</small>": { |
| "english": "Invoice #4471 never arrived<small><bdi>Fiona Reid</bdi> · 09:12</small>", |
| "locales": [ |
| @@ -1951,6 +1999,22 @@ |
| "zh-Hans" |
| ] |
| }, |
| + "samples.List": { |
| + "english": "List", |
| + "locales": [ |
| + "ar", |
| + "bn", |
| + "es", |
| + "ga", |
| + "hi", |
| + "ja", |
| + "pt-PT", |
| + "ru", |
| + "vi", |
| + "yue", |
| + "zh-Hans" |
| + ] |
| + }, |
| "samples.Made with Fiesta": { |
| "english": "Made with Fiesta", |
| "locales": [ |
| @@ -2191,6 +2255,22 @@ |
| "zh-Hans" |
| ] |
| }, |
| + "samples.View (noun)": { |
| + "english": "View", |
| + "locales": [ |
| + "ar", |
| + "bn", |
| + "es", |
| + "ga", |
| + "hi", |
| + "ja", |
| + "pt-PT", |
| + "ru", |
| + "vi", |
| + "yue", |
| + "zh-Hans" |
| + ] |
| + }, |
| "samples.We sent a link to <bdi>grace@example.com</bdi>. The link works once and expires soon.": { |
| "english": "We sent a link to <bdi>grace@example.com</bdi>. The link works once and expires soon.", |
| "locales": [ |
diff --git a/internal/gallery/i18n/es.json b/internal/gallery/i18n/es.json| index f042602..5145065 100644 |
| --- a/internal/gallery/i18n/es.json |
| +++ b/internal/gallery/i18n/es.json |
| @@ -95,7 +95,8 @@ |
| "examples.scheme-menu.blurb": "El selector de esquema de color: un menú details de formularios POST de un solo campo para Claro, Oscuro y Sistema. Enviada, la elección va a tu aplicación; por su cuenta, fiesta.js la recuerda en el navegador.", |
| "examples.scheme-menu.states.standalone": "Sin aplicación detrás: los formularios no tienen action, y con data-rst-scheme-store=\"local\" en el elemento <html> fiesta.js recuerda la elección en el navegador. La cabecera de la propia galería usa este.", |
| "examples.scheme-menu.states.three-choices-dark-current": "Cada esquema es un formulario de un solo campo, así que elegir uno es un POST que responde tu aplicación, y la página vuelve con el esquema elegido marcado como actual.", |
| - "examples.seg-tabs.blurb": "Un control segmentado de enlaces normales. La pestaña actual es aria-current en un enlace.", |
| + "examples.seg-tabs.blurb": "Un control segmentado de enlaces o botones. El enlace actual es aria-current; el botón pulsado es aria-pressed=\"true\".", |
| + "examples.seg-tabs.states.buttons-that-switch-in-place": "Botones, para una elección que cambia la página sin salir de ella, como una vista. Tu script mueve aria-pressed al botón pulsado.", |
| "examples.selbox.blurb": "La casilla de selección que lleva una fila de lista en modo selección. Su etiqueta repite la identidad de la fila.", |
| "examples.shell-sidebar.blurb": "El armazón del shell sidebar en una página de contenido: en un teléfono, un control para volver al índice.", |
| "examples.shell-topbar.blurb": "El armazón propio del contenedor con barra superior, como marcado. La página entera a la que pertenece es layout-topbar, en Armazones.", |
| @@ -369,6 +370,7 @@ |
| "labels.row-menu.links-only-for-a-list-inside-a-selection-form": "Solo enlaces, para una lista dentro de un formulario de selección", |
| "labels.scheme-menu.standalone": "Por su cuenta: la página recuerda la elección", |
| "labels.scheme-menu.three-choices-dark-current": "Tres opciones, con Oscuro como actual.", |
| + "labels.seg-tabs.buttons-that-switch-in-place": "Botones que cambian sin salir de la página", |
| "labels.seg-tabs.two-sections": "Dos secciones", |
| "labels.signin.a-passkey": "Una clave de acceso", |
| "labels.signin.after-the-link-is-sent": "Después de enviar el enlace", |
diff --git a/internal/gallery/i18n/ga.json b/internal/gallery/i18n/ga.json| index a968d5c..06dc146 100644 |
| --- a/internal/gallery/i18n/ga.json |
| +++ b/internal/gallery/i18n/ga.json |
| @@ -95,7 +95,8 @@ |
| "examples.scheme-menu.blurb": "An lasc scéime dathanna: roghchlár details d'fhoirmeacha POST aon réimse do Sorcha, Dorcha agus Córas. Nuair a phostáiltear í, téann an rogha chuig d'aip; ina haonar, cuimhníonn fiesta.js uirthi sa bhrabhsálaí.", |
| "examples.scheme-menu.states.standalone": "Gan aip taobh thiar de: níl action ag na foirmeacha, agus le data-rst-scheme-store=\"local\" ar an eilimint <html> cuimhníonn fiesta.js ar an rogha sa bhrabhsálaí. Úsáideann ceanntásc an ghailearaí féin an ceann seo.", |
| "examples.scheme-menu.states.three-choices-dark-current": "Is foirm aon réimse gach scéim, mar sin is POST a fhreagraíonn d'aip é ceann a roghnú, agus tagann an leathanach ar ais leis an scéim roghnaithe marcáilte mar an ceann reatha.", |
| - "examples.seg-tabs.blurb": "Rialtán deighilte de ghnáthnaisc. Is é an cluaisín reatha aria-current ar nasc.", |
| + "examples.seg-tabs.blurb": "Rialtán deighilte de naisc nó de chnaipí. Is é an nasc reatha aria-current; is é an cnaipe brúite aria-pressed=\"true\".", |
| + "examples.seg-tabs.states.buttons-that-switch-in-place": "Cnaipí, do rogha a athraíonn an leathanach san áit, mar radharc. Bogann do script aria-pressed chuig an gcnaipe a brúdh.", |
| "examples.selbox.blurb": "An ticbhosca roghnaithe a chaitheann ró liosta sa mhodh roghnaithe. Athdheimhníonn a lipéad céannacht an ró.", |
| "examples.shell-sidebar.blurb": "Creatlach na blaoisce sidebar ar leathanach ábhair: ar fhón, rialtán ar ais chuig an innéacs.", |
| "examples.shell-topbar.blurb": "Fráma féin an bhlaoisc barra barr, mar mharcáil. Is é layout-topbar an leathanach iomlán lena mbaineann sé, faoi Blaoscanna.", |
| @@ -369,6 +370,7 @@ |
| "labels.row-menu.links-only-for-a-list-inside-a-selection-form": "Naisc amháin, do liosta taobh istigh d'fhoirm roghnúcháin", |
| "labels.scheme-menu.standalone": "Ina aonar: cuimhníonn an leathanach ar an rogha", |
| "labels.scheme-menu.three-choices-dark-current": "Trí rogha, Dorcha reatha.", |
| + "labels.seg-tabs.buttons-that-switch-in-place": "Cnaipí a athraíonn san áit", |
| "labels.seg-tabs.two-sections": "Dhá rannóg", |
| "labels.signin.a-passkey": "Eochair rochtana", |
| "labels.signin.after-the-link-is-sent": "Tar éis an nasc a sheoladh", |
diff --git a/internal/gallery/i18n/hi.json b/internal/gallery/i18n/hi.json| index f27d0ae..b0de9a0 100644 |
| --- a/internal/gallery/i18n/hi.json |
| +++ b/internal/gallery/i18n/hi.json |
| @@ -95,7 +95,8 @@ |
| "examples.scheme-menu.blurb": "रंग योजना का स्विच: \"हल्का\", \"गहरा\" और \"सिस्टम\" के लिए एक-फ़ील्ड वाले POST फ़ॉर्म का details मेन्यू। भेजे जाने पर चुनाव आपके ऐप तक जाता है; अकेले में fiesta.js उसे ब्राउज़र में याद रखता है।", |
| "examples.scheme-menu.states.standalone": "पीछे कोई ऐप नहीं: फ़ॉर्म में कोई action नहीं है, और <html> तत्व पर data-rst-scheme-store=\"local\" होने से fiesta.js चुनाव को ब्राउज़र में याद रखता है। गैलरी का अपना हेडर यही इस्तेमाल करता है।", |
| "examples.scheme-menu.states.three-choices-dark-current": "हर योजना एक-फ़ील्ड वाला फ़ॉर्म है, इसलिए एक चुनना एक POST है जिसका जवाब आपका ऐप देता है, और पेज चुनी गई योजना को मौजूदा के रूप में चिह्नित करके लौटता है।", |
| - "examples.seg-tabs.blurb": "सादे लिंक से बना एक खंडित नियंत्रण। मौजूदा टैब किसी लिंक पर aria-current है।", |
| + "examples.seg-tabs.blurb": "लिंक या बटन से बना एक खंडित नियंत्रण। मौजूदा लिंक aria-current है; दबाया गया बटन aria-pressed=\"true\" है।", |
| + "examples.seg-tabs.states.buttons-that-switch-in-place": "बटन, ऐसे चुनाव के लिए जो पेज को उसी जगह बदल देता है, जैसे कोई व्यू। आपकी स्क्रिप्ट aria-pressed को दबाए गए बटन पर ले जाती है।", |
| "examples.selbox.blurb": "चयन मोड में सूची पंक्ति जो चेकबॉक्स पहनती है। उसका लेबल पंक्ति की पहचान दोहराता है।", |
| "examples.shell-sidebar.blurb": "किसी सामग्री पृष्ठ पर sidebar शेल का ढाँचा: फ़ोन पर, सूची पर वापस जाने का एक नियंत्रण।", |
| "examples.shell-topbar.blurb": "टॉपबार शेल का अपना ढाँचा, मार्कअप के रूप में। जिस पूरे पेज का यह हिस्सा है, वह layout-topbar है, शेल के अंतर्गत।", |
| @@ -369,6 +370,7 @@ |
| "labels.row-menu.links-only-for-a-list-inside-a-selection-form": "सिर्फ़ लिंक, किसी चयन फ़ॉर्म के अंदर की सूची के लिए", |
| "labels.scheme-menu.standalone": "अकेले: पेज चुनाव याद रखता है", |
| "labels.scheme-menu.three-choices-dark-current": "तीन विकल्प, मौजूदा गहरा।", |
| + "labels.seg-tabs.buttons-that-switch-in-place": "उसी जगह बदलने वाले बटन", |
| "labels.seg-tabs.two-sections": "दो खंड", |
| "labels.signin.a-passkey": "एक पासकी", |
| "labels.signin.after-the-link-is-sent": "लिंक भेजे जाने के बाद", |
diff --git a/internal/gallery/i18n/ja.json b/internal/gallery/i18n/ja.json| index d657fd1..c75a9ae 100644 |
| --- a/internal/gallery/i18n/ja.json |
| +++ b/internal/gallery/i18n/ja.json |
| @@ -95,7 +95,8 @@ |
| "examples.scheme-menu.blurb": "配色の切り替えです。「ライト」「ダーク」「システムに合わせる」の 1 項目 POST フォームを並べた details メニュー。送信すれば選択はアプリへ届き、単体なら fiesta.js がブラウザーに覚えておきます。", |
| "examples.scheme-menu.states.standalone": "背後にアプリはありません。フォームに action はなく、<html> 要素に data-rst-scheme-store=\"local\" があると fiesta.js が選択をブラウザーに覚えておきます。ギャラリー自身のヘッダーはこれを使っています。", |
| "examples.scheme-menu.states.three-choices-dark-current": "配色はそれぞれ一項目のフォームなので、選ぶことはアプリが応答する POST になり、ページは選んだ配色を現在のものとして印を付けて戻ってきます。", |
| - "examples.seg-tabs.blurb": "ただのリンクによるセグメント切り替え。現在のタブはリンクに付いた aria-current です。", |
| + "examples.seg-tabs.blurb": "リンクまたはボタンによるセグメント切り替え。現在のリンクは aria-current、押されたボタンは aria-pressed=\"true\" です。", |
| + "examples.seg-tabs.states.buttons-that-switch-in-place": "表示形式のように、ページをその場で変える選択にはボタンを使います。押されたボタンへ aria-pressed を移すのはスクリプトの役目です。", |
| "examples.selbox.blurb": "選択モードで一覧の行が身につけるチェックボックスです。そのラベルは、その行が何であるかを言い直します。", |
| "examples.shell-sidebar.blurb": "コンテンツページでの sidebar シェルの枠です。スマートフォンでは、目次へ戻る操作になります。", |
| "examples.shell-topbar.blurb": "トップバーのシェル自身の枠組みを、マークアップとして示したもの。これが属するページ全体は、シェルの下の layout-topbar です。", |
| @@ -369,6 +370,7 @@ |
| "labels.row-menu.links-only-for-a-list-inside-a-selection-form": "リンクのみ。選択フォーム内のリスト向け", |
| "labels.scheme-menu.standalone": "単体:ページが選択を覚えておく", |
| "labels.scheme-menu.three-choices-dark-current": "3 つの選択肢。現在はダークです。", |
| + "labels.seg-tabs.buttons-that-switch-in-place": "その場で切り替えるボタン", |
| "labels.seg-tabs.two-sections": "セクションが二つ", |
| "labels.signin.a-passkey": "パスキー", |
| "labels.signin.after-the-link-is-sent": "リンクを送ったあと", |
diff --git a/internal/gallery/i18n/pt-PT.json b/internal/gallery/i18n/pt-PT.json| index 43e03d5..07752dc 100644 |
| --- a/internal/gallery/i18n/pt-PT.json |
| +++ b/internal/gallery/i18n/pt-PT.json |
| @@ -95,7 +95,8 @@ |
| "examples.scheme-menu.blurb": "O seletor de esquema de cores: um menu details de formulários POST de um só campo para Claro, Escuro e Sistema. Enviada, a escolha vai para a sua aplicação; isolado, o fiesta.js lembra-a no navegador.", |
| "examples.scheme-menu.states.standalone": "Sem aplicação por trás: os formulários não têm action, e com data-rst-scheme-store=\"local\" no elemento <html> o fiesta.js lembra a escolha no navegador. O cabeçalho da própria galeria usa este.", |
| "examples.scheme-menu.states.three-choices-dark-current": "Cada esquema é um formulário de um só campo, por isso escolher um é um POST a que a sua aplicação responde, e a página volta com o esquema escolhido marcado como atual.", |
| - "examples.seg-tabs.blurb": "Um controlo segmentado de ligações simples. O separador atual é aria-current numa ligação.", |
| + "examples.seg-tabs.blurb": "Um controlo segmentado de ligações ou botões. A ligação atual é aria-current; o botão premido é aria-pressed=\"true\".", |
| + "examples.seg-tabs.states.buttons-that-switch-in-place": "Botões, para uma escolha que muda a página no mesmo sítio, como uma vista. O seu script passa aria-pressed para o botão premido.", |
| "examples.selbox.blurb": "A caixa de seleção que uma linha de lista usa em modo de seleção. A sua etiqueta repete a identidade da linha.", |
| "examples.shell-sidebar.blurb": "A moldura do invólucro sidebar numa página de conteúdo: num telemóvel, um controlo para voltar ao índice.", |
| "examples.shell-topbar.blurb": "A estrutura própria do invólucro com barra superior, como marcação. A página inteira a que pertence é layout-topbar, em Invólucros.", |
| @@ -369,6 +370,7 @@ |
| "labels.row-menu.links-only-for-a-list-inside-a-selection-form": "Só links, para uma lista dentro de um formulário de seleção", |
| "labels.scheme-menu.standalone": "Isolado: a página lembra a escolha", |
| "labels.scheme-menu.three-choices-dark-current": "Três opções, com Escuro como atual.", |
| + "labels.seg-tabs.buttons-that-switch-in-place": "Botões que mudam no mesmo sítio", |
| "labels.seg-tabs.two-sections": "Duas secções", |
| "labels.signin.a-passkey": "Uma chave de acesso", |
| "labels.signin.after-the-link-is-sent": "Depois de enviar a ligação", |
diff --git a/internal/gallery/i18n/ru.json b/internal/gallery/i18n/ru.json| index 0ad24ed..d34d4f4 100644 |
| --- a/internal/gallery/i18n/ru.json |
| +++ b/internal/gallery/i18n/ru.json |
| @@ -95,7 +95,8 @@ |
| "examples.scheme-menu.blurb": "Переключатель цветовой схемы: меню details из POST-форм с одним полем для вариантов «Светлая», «Тёмная» и «Как в системе». При отправке выбор уходит вашему приложению; без него fiesta.js запоминает выбор в браузере.", |
| "examples.scheme-menu.states.standalone": "За ним нет приложения: у форм нет action, а с data-rst-scheme-store=\"local\" на элементе <html> fiesta.js запоминает выбор в браузере. Этот вариант использует шапка самой галереи.", |
| "examples.scheme-menu.states.three-choices-dark-current": "Каждая схема — форма с одним полем, так что выбор схемы — это POST, на который отвечает ваше приложение, и страница возвращается с выбранной схемой, отмеченной как текущая.", |
| - "examples.seg-tabs.blurb": "Сегментированный переключатель из обычных ссылок. Текущая вкладка — это aria-current на ссылке.", |
| + "examples.seg-tabs.blurb": "Сегментированный переключатель из ссылок или кнопок. Текущая ссылка — это aria-current, нажатая кнопка — aria-pressed=\"true\".", |
| + "examples.seg-tabs.states.buttons-that-switch-in-place": "Кнопки — для выбора, который меняет страницу на месте, например вид. Ваш скрипт переносит aria-pressed на нажатую кнопку.", |
| "examples.selbox.blurb": "Флажок выбора, который строка списка носит в режиме выбора. Его подпись повторяет, что это за строка.", |
| "examples.shell-sidebar.blurb": "Обрамление оболочки sidebar на странице с содержимым: на телефоне это кнопка возврата к оглавлению.", |
| "examples.shell-topbar.blurb": "Собственная обвязка оболочки с верхней панелью, в виде разметки. Целая страница, к которой она относится, — layout-topbar в разделе «Оболочки».", |
| @@ -369,6 +370,7 @@ |
| "labels.row-menu.links-only-for-a-list-inside-a-selection-form": "Только ссылки, для списка внутри формы выбора", |
| "labels.scheme-menu.standalone": "Сам по себе: страница запоминает выбор", |
| "labels.scheme-menu.three-choices-dark-current": "Три варианта, выбрана Тёмная.", |
| + "labels.seg-tabs.buttons-that-switch-in-place": "Кнопки, которые переключают на месте", |
| "labels.seg-tabs.two-sections": "Два раздела", |
| "labels.signin.a-passkey": "Ключ доступа", |
| "labels.signin.after-the-link-is-sent": "После отправки ссылки", |
diff --git a/internal/gallery/i18n/samples.ar.json b/internal/gallery/i18n/samples.ar.json| index db2d344..322dfd9 100644 |
| --- a/internal/gallery/i18n/samples.ar.json |
| +++ b/internal/gallery/i18n/samples.ar.json |
| @@ -140,6 +140,7 @@ |
| "From": "من", |
| "Full name": "الاسم الكامل", |
| "Go back": "رجوع", |
| + "Grid": "شبكة", |
| "Hanoi": "هانوي", |
| "Harbour logo": "شعار Harbour", |
| "Headline": "العنوان الرئيسي", |
| @@ -161,6 +162,7 @@ |
| "Light": "فاتح", |
| "Lima": "ليما", |
| "Lisbon": "لشبونة", |
| + "List": "قائمة", |
| "Live": "مباشر", |
| "Local time.": "بالتوقيت المحلي.", |
| "Made with Fiesta": "صُنع باستخدام Fiesta", |
| @@ -343,6 +345,7 @@ |
| "Use your email instead": "استخدم بريدك الإلكتروني بدلًا من ذلك", |
| "Valletta": "فاليتا", |
| "View": "عرض", |
| + "View (noun)": "طريقة العرض", |
| "View orders": "عرض الطلبات", |
| "Waiting": "في الانتظار", |
| "Waiting on us": "بانتظار ردّنا", |
diff --git a/internal/gallery/i18n/samples.bn.json b/internal/gallery/i18n/samples.bn.json| index c5fe4c9..1f59de2 100644 |
| --- a/internal/gallery/i18n/samples.bn.json |
| +++ b/internal/gallery/i18n/samples.bn.json |
| @@ -140,6 +140,7 @@ |
| "From": "থেকে", |
| "Full name": "পুরো নাম", |
| "Go back": "ফিরে যান", |
| + "Grid": "গ্রিড", |
| "Hanoi": "হ্যানয়", |
| "Harbour logo": "Harbour লোগো", |
| "Headline": "মূল শিরোনাম", |
| @@ -161,6 +162,7 @@ |
| "Light": "হালকা", |
| "Lima": "লিমা", |
| "Lisbon": "লিসবন", |
| + "List": "তালিকা", |
| "Live": "চালু", |
| "Local time.": "স্থানীয় সময়।", |
| "Made with Fiesta": "Fiesta দিয়ে তৈরি", |
| @@ -343,6 +345,7 @@ |
| "Use your email instead": "বরং আপনার ইমেল ব্যবহার করুন", |
| "Valletta": "ভাল্লেত্তা", |
| "View": "দেখুন", |
| + "View (noun)": "ভিউ", |
| "View orders": "অর্ডার দেখুন", |
| "Waiting": "অপেক্ষায়", |
| "Waiting on us": "আমাদের অপেক্ষায়", |
diff --git a/internal/gallery/i18n/samples.es.json b/internal/gallery/i18n/samples.es.json| index 424e0fc..67af1fd 100644 |
| --- a/internal/gallery/i18n/samples.es.json |
| +++ b/internal/gallery/i18n/samples.es.json |
| @@ -140,6 +140,7 @@ |
| "From": "Desde", |
| "Full name": "Nombre completo", |
| "Go back": "Volver", |
| + "Grid": "Cuadrícula", |
| "Hanoi": "Hanói", |
| "Harbour logo": "Logotipo de Harbour", |
| "Headline": "Titular", |
| @@ -161,6 +162,7 @@ |
| "Light": "Claro", |
| "Lima": "Lima", |
| "Lisbon": "Lisboa", |
| + "List": "Lista", |
| "Live": "En vivo", |
| "Local time.": "Hora local.", |
| "Made with Fiesta": "Hecho con Fiesta", |
| @@ -343,6 +345,7 @@ |
| "Use your email instead": "Usar tu correo electrónico", |
| "Valletta": "La Valeta", |
| "View": "Ver", |
| + "View (noun)": "Vista", |
| "View orders": "Ver pedidos", |
| "Waiting": "En espera", |
| "Waiting on us": "Esperándonos", |
diff --git a/internal/gallery/i18n/samples.ga.json b/internal/gallery/i18n/samples.ga.json| index 9c874c4..041118f 100644 |
| --- a/internal/gallery/i18n/samples.ga.json |
| +++ b/internal/gallery/i18n/samples.ga.json |
| @@ -140,6 +140,7 @@ |
| "From": "Ó", |
| "Full name": "Ainm iomlán", |
| "Go back": "Téigh ar ais", |
| + "Grid": "Greille", |
| "Hanoi": "Hanoi", |
| "Harbour logo": "Lógó Harbour", |
| "Headline": "Ceannlíne", |
| @@ -161,6 +162,7 @@ |
| "Light": "Sorcha", |
| "Lima": "Líoma", |
| "Lisbon": "Liospóin", |
| + "List": "Liosta", |
| "Live": "Beo", |
| "Local time.": "Am áitiúil.", |
| "Made with Fiesta": "Déanta le Fiesta", |
| @@ -343,6 +345,7 @@ |
| "Use your email instead": "Úsáid do ríomhphost ina ionad", |
| "Valletta": "Vaileite", |
| "View": "Féach", |
| + "View (noun)": "Radharc", |
| "View orders": "Féach ar na horduithe", |
| "Waiting": "Ag fanacht", |
| "Waiting on us": "Ag fanacht orainn", |
diff --git a/internal/gallery/i18n/samples.hi.json b/internal/gallery/i18n/samples.hi.json| index 6f404db..96e57fb 100644 |
| --- a/internal/gallery/i18n/samples.hi.json |
| +++ b/internal/gallery/i18n/samples.hi.json |
| @@ -140,6 +140,7 @@ |
| "From": "से", |
| "Full name": "पूरा नाम", |
| "Go back": "वापस जाएँ", |
| + "Grid": "ग्रिड", |
| "Hanoi": "हनोई", |
| "Harbour logo": "Harbour लोगो", |
| "Headline": "मुख्य शीर्षक", |
| @@ -161,6 +162,7 @@ |
| "Light": "हल्का", |
| "Lima": "लीमा", |
| "Lisbon": "लिस्बन", |
| + "List": "सूची", |
| "Live": "लाइव", |
| "Local time.": "स्थानीय समय।", |
| "Made with Fiesta": "Fiesta से बना", |
| @@ -343,6 +345,7 @@ |
| "Use your email instead": "इसके बजाय अपना ईमेल इस्तेमाल करें", |
| "Valletta": "वैलेटा", |
| "View": "देखें", |
| + "View (noun)": "व्यू", |
| "View orders": "ऑर्डर देखें", |
| "Waiting": "इंतज़ार में", |
| "Waiting on us": "हमारे जवाब का इंतज़ार", |
diff --git a/internal/gallery/i18n/samples.ja.json b/internal/gallery/i18n/samples.ja.json| index e300538..5c0d872 100644 |
| --- a/internal/gallery/i18n/samples.ja.json |
| +++ b/internal/gallery/i18n/samples.ja.json |
| @@ -140,6 +140,7 @@ |
| "From": "開始日", |
| "Full name": "氏名", |
| "Go back": "戻る", |
| + "Grid": "グリッド", |
| "Hanoi": "ハノイ", |
| "Harbour logo": "Harbour のロゴ", |
| "Headline": "見出し", |
| @@ -161,6 +162,7 @@ |
| "Light": "ライト", |
| "Lima": "リマ", |
| "Lisbon": "リスボン", |
| + "List": "リスト", |
| "Live": "公開中", |
| "Local time.": "現地時間。", |
| "Made with Fiesta": "Fiesta で作成", |
| @@ -343,6 +345,7 @@ |
| "Use your email instead": "代わりにメールアドレスを使う", |
| "Valletta": "バレッタ", |
| "View": "表示", |
| + "View (noun)": "表示形式", |
| "View orders": "注文を見る", |
| "Waiting": "対応待ち", |
| "Waiting on us": "こちらの対応待ち", |
diff --git a/internal/gallery/i18n/samples.pt-PT.json b/internal/gallery/i18n/samples.pt-PT.json| index bec6191..057dfd2 100644 |
| --- a/internal/gallery/i18n/samples.pt-PT.json |
| +++ b/internal/gallery/i18n/samples.pt-PT.json |
| @@ -140,6 +140,7 @@ |
| "From": "De", |
| "Full name": "Nome completo", |
| "Go back": "Voltar", |
| + "Grid": "Grelha", |
| "Hanoi": "Hanói", |
| "Harbour logo": "Logótipo Harbour", |
| "Headline": "Título principal", |
| @@ -161,6 +162,7 @@ |
| "Light": "Claro", |
| "Lima": "Lima", |
| "Lisbon": "Lisboa", |
| + "List": "Lista", |
| "Live": "Ativo", |
| "Local time.": "Hora local.", |
| "Made with Fiesta": "Feito com Fiesta", |
| @@ -343,6 +345,7 @@ |
| "Use your email instead": "Usar o seu e-mail", |
| "Valletta": "Valeta", |
| "View": "Ver", |
| + "View (noun)": "Vista", |
| "View orders": "Ver encomendas", |
| "Waiting": "Em espera", |
| "Waiting on us": "À nossa espera", |
diff --git a/internal/gallery/i18n/samples.ru.json b/internal/gallery/i18n/samples.ru.json| index e80552e..5ed471c 100644 |
| --- a/internal/gallery/i18n/samples.ru.json |
| +++ b/internal/gallery/i18n/samples.ru.json |
| @@ -140,6 +140,7 @@ |
| "From": "С", |
| "Full name": "Полное имя", |
| "Go back": "Назад", |
| + "Grid": "Сетка", |
| "Hanoi": "Ханой", |
| "Harbour logo": "Логотип Harbour", |
| "Headline": "Заголовок", |
| @@ -161,6 +162,7 @@ |
| "Light": "Светлая", |
| "Lima": "Лима", |
| "Lisbon": "Лиссабон", |
| + "List": "Список", |
| "Live": "Активно", |
| "Local time.": "Местное время.", |
| "Made with Fiesta": "Сделано на Fiesta", |
| @@ -343,6 +345,7 @@ |
| "Use your email instead": "Войти по электронной почте", |
| "Valletta": "Валлетта", |
| "View": "Открыть", |
| + "View (noun)": "Вид", |
| "View orders": "Посмотреть заказы", |
| "Waiting": "Ждёт нас", |
| "Waiting on us": "Ждут нашего ответа", |
diff --git a/internal/gallery/i18n/samples.vi.json b/internal/gallery/i18n/samples.vi.json| index 754082e..a629f56 100644 |
| --- a/internal/gallery/i18n/samples.vi.json |
| +++ b/internal/gallery/i18n/samples.vi.json |
| @@ -140,6 +140,7 @@ |
| "From": "Từ", |
| "Full name": "Họ và tên", |
| "Go back": "Quay lại", |
| + "Grid": "Lưới", |
| "Hanoi": "Hà Nội", |
| "Harbour logo": "Logo Harbour", |
| "Headline": "Dòng tiêu đề", |
| @@ -161,6 +162,7 @@ |
| "Light": "Sáng", |
| "Lima": "Lima", |
| "Lisbon": "Lisbon", |
| + "List": "Danh sách", |
| "Live": "Đang chạy", |
| "Local time.": "Giờ địa phương.", |
| "Made with Fiesta": "Làm bằng Fiesta", |
| @@ -343,6 +345,7 @@ |
| "Use your email instead": "Dùng email của bạn thay thế", |
| "Valletta": "Valletta", |
| "View": "Xem", |
| + "View (noun)": "Kiểu xem", |
| "View orders": "Xem đơn hàng", |
| "Waiting": "Đang chờ", |
| "Waiting on us": "Đang chờ chúng ta", |
diff --git a/internal/gallery/i18n/samples.yue.json b/internal/gallery/i18n/samples.yue.json| index 4af312e..fb784ed 100644 |
| --- a/internal/gallery/i18n/samples.yue.json |
| +++ b/internal/gallery/i18n/samples.yue.json |
| @@ -140,6 +140,7 @@ |
| "From": "由", |
| "Full name": "全名", |
| "Go back": "返去", |
| + "Grid": "格狀", |
| "Hanoi": "河內", |
| "Harbour logo": "Harbour 標誌", |
| "Headline": "大標題", |
| @@ -161,6 +162,7 @@ |
| "Light": "淺色", |
| "Lima": "利馬", |
| "Lisbon": "里斯本", |
| + "List": "清單", |
| "Live": "上線", |
| "Local time.": "當地時間。", |
| "Made with Fiesta": "用 Fiesta 整", |
| @@ -343,6 +345,7 @@ |
| "Use your email instead": "改用電郵", |
| "Valletta": "華列他", |
| "View": "睇", |
| + "View (noun)": "檢視方式", |
| "View orders": "睇訂單", |
| "Waiting": "等緊", |
| "Waiting on us": "等緊我哋", |
diff --git a/internal/gallery/i18n/samples.zh-Hans.json b/internal/gallery/i18n/samples.zh-Hans.json| index 0283723..e81d782 100644 |
| --- a/internal/gallery/i18n/samples.zh-Hans.json |
| +++ b/internal/gallery/i18n/samples.zh-Hans.json |
| @@ -140,6 +140,7 @@ |
| "From": "从", |
| "Full name": "全名", |
| "Go back": "返回", |
| + "Grid": "网格", |
| "Hanoi": "河内", |
| "Harbour logo": "Harbour 标志", |
| "Headline": "大标题", |
| @@ -161,6 +162,7 @@ |
| "Light": "浅色", |
| "Lima": "利马", |
| "Lisbon": "里斯本", |
| + "List": "列表", |
| "Live": "上线", |
| "Local time.": "当地时间。", |
| "Made with Fiesta": "使用 Fiesta 制作", |
| @@ -343,6 +345,7 @@ |
| "Use your email instead": "改用电子邮件", |
| "Valletta": "瓦莱塔", |
| "View": "查看", |
| + "View (noun)": "视图", |
| "View orders": "查看订单", |
| "Waiting": "待我们处理", |
| "Waiting on us": "等我们处理", |
diff --git a/internal/gallery/i18n/vi.json b/internal/gallery/i18n/vi.json| index 3b162ed..792cb34 100644 |
| --- a/internal/gallery/i18n/vi.json |
| +++ b/internal/gallery/i18n/vi.json |
| @@ -95,7 +95,8 @@ |
| "examples.scheme-menu.blurb": "Công tắc bảng màu: một menu details gồm các biểu mẫu POST một trường cho Sáng, Tối và Theo hệ thống. Khi gửi đi, lựa chọn tới ứng dụng của bạn; khi đứng riêng, fiesta.js ghi nhớ nó trong trình duyệt.", |
| "examples.scheme-menu.states.standalone": "Không có ứng dụng phía sau: các biểu mẫu không có action, và với data-rst-scheme-store=\"local\" trên phần tử <html>, fiesta.js ghi nhớ lựa chọn trong trình duyệt. Phần đầu trang của chính thư viện dùng cái này.", |
| "examples.scheme-menu.states.three-choices-dark-current": "Mỗi bảng màu là một biểu mẫu một trường, nên chọn một bảng màu là một yêu cầu POST mà ứng dụng của bạn trả lời, và trang quay lại với bảng màu đã chọn được đánh dấu là hiện tại.", |
| - "examples.seg-tabs.blurb": "Một điều khiển phân đoạn gồm các liên kết thường. Thẻ hiện tại là aria-current trên một liên kết.", |
| + "examples.seg-tabs.blurb": "Một điều khiển phân đoạn gồm các liên kết hoặc nút. Liên kết hiện tại là aria-current; nút đang được nhấn là aria-pressed=\"true\".", |
| + "examples.seg-tabs.states.buttons-that-switch-in-place": "Nút, cho một lựa chọn thay đổi trang ngay tại chỗ, như một kiểu xem. Script của bạn chuyển aria-pressed sang nút được nhấn.", |
| "examples.selbox.blurb": "Hộp kiểm chọn mà một hàng danh sách mang khi ở chế độ chọn. Nhãn của nó nhắc lại danh tính của hàng.", |
| "examples.shell-sidebar.blurb": "Bộ khung của shell sidebar trên một trang nội dung: trên điện thoại, đó là nút quay lại trang mục lục.", |
| "examples.shell-topbar.blurb": "Phần khung riêng của vỏ thanh trên cùng, dưới dạng mã đánh dấu. Trang hoàn chỉnh mà nó thuộc về là layout-topbar, trong mục Khung trang.", |
| @@ -369,6 +370,7 @@ |
| "labels.row-menu.links-only-for-a-list-inside-a-selection-form": "Chỉ liên kết, cho danh sách nằm trong biểu mẫu chọn", |
| "labels.scheme-menu.standalone": "Độc lập: trang ghi nhớ lựa chọn", |
| "labels.scheme-menu.three-choices-dark-current": "Ba lựa chọn, Tối đang được chọn.", |
| + "labels.seg-tabs.buttons-that-switch-in-place": "Nút chuyển đổi ngay tại chỗ", |
| "labels.seg-tabs.two-sections": "Hai mục", |
| "labels.signin.a-passkey": "Một passkey", |
| "labels.signin.after-the-link-is-sent": "Sau khi gửi liên kết", |
diff --git a/internal/gallery/i18n/yue.json b/internal/gallery/i18n/yue.json| index 7dd81ac..3c6d5e1 100644 |
| --- a/internal/gallery/i18n/yue.json |
| +++ b/internal/gallery/i18n/yue.json |
| @@ -95,7 +95,8 @@ |
| "examples.scheme-menu.blurb": "配色切換:一個 details 選單,入面係「淺色」、「深色」同「跟系統」嘅單一欄位 POST 表格。提交嘅話,選擇會送去你個應用程式;獨立用嘅話,fiesta.js 會喺瀏覽器記住佢。", |
| "examples.scheme-menu.states.standalone": "後面冇應用程式:啲表格冇 action,而 <html> 元素上面有 data-rst-scheme-store=\"local\" 嘅話,fiesta.js 會喺瀏覽器記住個選擇。圖庫自己嘅頁頭用緊呢個。", |
| "examples.scheme-menu.states.three-choices-dark-current": "每個配色都係一個單一欄位嘅表格,所以揀一個就係一個由你個應用程式回應嘅 POST,頁面返嚟嗰陣會將揀咗嘅配色標做目前。", |
| - "examples.seg-tabs.blurb": "由普通連結組成嘅分段控制。目前嘅分頁就係連結上面嘅 aria-current。", |
| + "examples.seg-tabs.blurb": "由連結或者按鈕組成嘅分段控制。目前嘅連結係 aria-current;撳咗嘅按鈕係 aria-pressed=\"true\"。", |
| + "examples.seg-tabs.states.buttons-that-switch-in-place": "按鈕,用嚟做喺原位改變頁面嘅選擇,例如檢視方式。你嘅腳本會將 aria-pressed 移去撳咗嘅按鈕。", |
| "examples.selbox.blurb": "喺揀選模式下,清單一行戴住嘅勾選框。佢個標籤會將呢行嘅身分再講多次。", |
| "examples.shell-sidebar.blurb": "sidebar 外殼喺內容頁上嘅框架:喺手機上,係一個返去目錄嘅控制。", |
| "examples.shell-topbar.blurb": "頂欄外殼自己嘅框架,以標記形式呈現。佢所屬嘅成版係頁面外殼下面嘅 layout-topbar。", |
| @@ -369,6 +370,7 @@ |
| "labels.row-menu.links-only-for-a-list-inside-a-selection-form": "只有連結,用喺選擇表格入面嘅清單", |
| "labels.scheme-menu.standalone": "獨立:頁面記住個選擇", |
| "labels.scheme-menu.three-choices-dark-current": "三個選擇,目前係深色。", |
| + "labels.seg-tabs.buttons-that-switch-in-place": "喺原位切換嘅按鈕", |
| "labels.seg-tabs.two-sections": "兩個分區", |
| "labels.signin.a-passkey": "通行密鑰", |
| "labels.signin.after-the-link-is-sent": "寄咗連結之後", |
diff --git a/internal/gallery/i18n/zh-Hans.json b/internal/gallery/i18n/zh-Hans.json| index bf46f9e..5b66eea 100644 |
| --- a/internal/gallery/i18n/zh-Hans.json |
| +++ b/internal/gallery/i18n/zh-Hans.json |
| @@ -95,7 +95,8 @@ |
| "examples.scheme-menu.blurb": "配色切换:一个 details 菜单,里面是「浅色」「深色」「跟随系统」的单字段 POST 表单。提交时,选择发送到你的应用;独立使用时,fiesta.js 在浏览器里记住它。", |
| "examples.scheme-menu.states.standalone": "背后没有应用:这些表单没有 action,而 <html> 元素上有 data-rst-scheme-store=\"local\" 时,fiesta.js 会在浏览器里记住选择。图库自己的页眉用的就是这个。", |
| "examples.scheme-menu.states.three-choices-dark-current": "每种配色都是一个单字段表单,所以选一种就是一次由你的应用回应的 POST,页面返回时会把选中的配色标为当前。", |
| - "examples.seg-tabs.blurb": "由普通链接组成的分段控件。当前标签页就是链接上的 aria-current。", |
| + "examples.seg-tabs.blurb": "由链接或按钮组成的分段控件。当前链接是 aria-current;按下的按钮是 aria-pressed=\"true\"。", |
| + "examples.seg-tabs.states.buttons-that-switch-in-place": "按钮,用于在原地改变页面的选择,比如视图。你的脚本会把 aria-pressed 移到按下的按钮上。", |
| "examples.selbox.blurb": "列表行在选择模式下佩戴的那个勾选框。它的标签会把这一行的身份再说一遍。", |
| "examples.shell-sidebar.blurb": "sidebar 外壳在内容页上的框架:在手机上,是一个返回目录的控件。", |
| "examples.shell-topbar.blurb": "顶栏外壳自己的框架,以标记形式呈现。它所属的整页是页面外壳下的 layout-topbar。", |
| @@ -369,6 +370,7 @@ |
| "labels.row-menu.links-only-for-a-list-inside-a-selection-form": "仅链接,用于选择表单中的列表", |
| "labels.scheme-menu.standalone": "独立:页面记住选择", |
| "labels.scheme-menu.three-choices-dark-current": "三个选项,当前为深色。", |
| + "labels.seg-tabs.buttons-that-switch-in-place": "原地切换的按钮", |
| "labels.seg-tabs.two-sections": "两个分区", |
| "labels.signin.a-passkey": "通行密钥", |
| "labels.signin.after-the-link-is-sent": "链接寄出之后", |
diff --git a/internal/gallery/localise.go b/internal/gallery/localise.go| index f112f05..3d27532 100644 |
| --- a/internal/gallery/localise.go |
| +++ b/internal/gallery/localise.go |
| @@ -119,6 +119,19 @@ func hasWords(s string) bool { |
| // verbSense. English still reads the bare word. |
| const verbSense = " (verb)" |
| +// nounSense is the other way round: a sample word that names a thing |
| +// where the catalog's bare word is the action. The seg-tabs group |
| +// "View" names the view a reader picks; the bare View is a row menu's |
| +// link, a verb in most of the eleven (Russian Открыть, open), which |
| +// would label the group as a command. |
| +const nounSense = " (noun)" |
| + |
| +// withoutSense is a sample key's English: the key with its sense, if |
| +// it has one, taken off. |
| +func withoutSense(key string) string { |
| + return strings.TrimSuffix(strings.TrimSuffix(key, verbSense), nounSense) |
| +} |
| + |
| // sampleSenses names, per example, the sample text whose key is not its |
| // English. Rastrillo marks the sense in its typed sample data |
| // (verb("Archive")); an example here is plain HTML, which cannot, so |
| @@ -130,6 +143,7 @@ const verbSense = " (verb)" |
| var sampleSenses = map[string]map[string]string{ |
| "row-menu": {"Archive": "Archive" + verbSense}, |
| "bulk-bar": {"Export": "Export" + verbSense}, |
| + "seg-tabs": {"View": "View" + nounSense}, |
| } |
| // sampleID is the id a sample string is traced under: its key, which is |
| @@ -174,7 +188,7 @@ func samplesIn(locale string) catalog { |
| // fails. Every lookup is traced. |
| func sampleText(locale, key string) string { |
| trace(sampleID(key)) |
| - en := strings.TrimSuffix(key, verbSense) |
| + en := withoutSense(key) |
| if locale == "en" { |
| return en |
| } |
| @@ -376,7 +390,7 @@ func sampleKeys(name, src string) []string { |
| var out []string |
| walkMarkup(localiser{locale: "en", senses: sampleSenses[name], look: func(key string) string { |
| out = append(out, key) |
| - return strings.TrimSuffix(key, verbSense) |
| + return withoutSense(key) |
| }}, src) |
| return out |
| } |
diff --git a/internal/gallery/localise_test.go b/internal/gallery/localise_test.go| index 6599b50..44eb1ff 100644 |
| --- a/internal/gallery/localise_test.go |
| +++ b/internal/gallery/localise_test.go |
| @@ -550,3 +550,25 @@ func TestAVerbAndItsNounAreTranslatedApart(t *testing.T) { |
| english["samples.Archive"+verbSense], english["samples.Archive"]) |
| } |
| } |
| + |
| +// TestANounAndItsVerbAreTranslatedApart is the same split the other |
| +// way round: the seg-tabs group's View names the view a reader picks, |
| +// and the list's row menu View opens a row. Russian says Вид and |
| +// Открыть; read under the bare word, the group would be labelled |
| +// "Open", a command, for every Russian screen reader. |
| +func TestANounAndItsVerbAreTranslatedApart(t *testing.T) { |
| + files := render(t, "") |
| + group := string(files["day/ru/frames/seg-tabs/buttons-that-switch-in-place.html"]) |
| + if !strings.Contains(group, `role="group" aria-label="Вид"`) { |
| + t.Errorf("day/ru's seg-tabs buttons frame does not label its group Вид") |
| + } |
| + rows := string(files["day/ru/frames/list-grid/"+mustEntry(t, "list-grid").Default+".html"]) |
| + if !strings.Contains(rows, `>Открыть</a>`) { |
| + t.Errorf("day/ru's list-grid frame does not show the row menu link Открыть") |
| + } |
| + english := englishStrings(t) |
| + if english["samples.View"+nounSense] != "View" || english["samples.View"] != "View" { |
| + t.Errorf("EnglishStrings lists View (noun) as %q and View as %q; want both, each saying View", |
| + english["samples.View"+nounSense], english["samples.View"]) |
| + } |
| +} |