Skip to content

Commit dbdc13c

Browse files
committed
feat: add atlas i18n language menu
1 parent 7a2f1c3 commit dbdc13c

7 files changed

Lines changed: 1297 additions & 96 deletions

File tree

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ YSL collects the public Yellowstone National Park sound library and turns it int
2020

2121
The atlas is a static listening interface built from this repository's media files.
2222

23+
It supports English, Simplified Chinese, Japanese, and Korean. Use the in-page language switcher to change player labels, specimen notes, theme navigation, and share copy without leaving the current sound.
24+
2325
```bash
2426
python -m http.server 8000
2527
```
@@ -103,6 +105,8 @@ YSL 收集 Yellowstone National Park 的公开声音库,并把这些音频和
103105

104106
### 本地预览
105107

108+
图谱支持英文、简体中文、日文与韩文。可以使用页面内的语言切换器,在不中断当前声音的情况下切换播放器标签、标本札记、主题导航与分享文案。
109+
106110
```bash
107111
python -m http.server 8000
108112
```

atlas/css/main.css

Lines changed: 256 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,7 @@ figure {
626626

627627
.stamp-rule-top {
628628
position: relative;
629+
flex-wrap: wrap;
629630
padding-right: 42px;
630631
}
631632

@@ -641,6 +642,154 @@ figure {
641642
white-space: nowrap;
642643
}
643644

645+
.language-switcher {
646+
position: relative;
647+
display: inline-block;
648+
margin-left: auto;
649+
}
650+
651+
.language-menu-button {
652+
min-height: 34px;
653+
min-width: 62px;
654+
display: inline-flex;
655+
align-items: center;
656+
justify-content: center;
657+
gap: 7px;
658+
padding: 0 10px;
659+
border: 1px solid var(--line-soft);
660+
border-radius: 999px;
661+
background:
662+
linear-gradient(180deg, color-mix(in oklch, var(--bone-high) 82%, transparent), color-mix(in oklch, var(--bone) 58%, transparent));
663+
color: var(--ink-soft);
664+
font-family: var(--font-mono);
665+
line-height: 1;
666+
transition:
667+
background-color 160ms ease,
668+
border-color 160ms ease,
669+
color 160ms ease,
670+
transform 160ms ease;
671+
}
672+
673+
.language-icon {
674+
width: 17px;
675+
height: 17px;
676+
flex: 0 0 auto;
677+
stroke-width: 1.8;
678+
}
679+
680+
.language-current-label {
681+
padding-left: 5px;
682+
border-left: 1px solid currentColor;
683+
font-size: 0.52rem;
684+
font-weight: 800;
685+
letter-spacing: 0.04em;
686+
opacity: 0.72;
687+
text-transform: uppercase;
688+
}
689+
690+
.language-menu-button:hover,
691+
.language-switcher.is-open .language-menu-button {
692+
border-color: color-mix(in oklch, var(--theme-color) 62%, var(--line));
693+
color: var(--ink);
694+
transform: translateY(-1px);
695+
}
696+
697+
.language-menu-button:active {
698+
transform: translateY(1px);
699+
}
700+
701+
.language-menu-button:focus-visible {
702+
outline: 2px solid var(--theme-color);
703+
outline-offset: 2px;
704+
}
705+
706+
.language-menu {
707+
position: absolute;
708+
right: 0;
709+
bottom: calc(100% + 10px);
710+
z-index: 24;
711+
width: 188px;
712+
padding: 6px;
713+
border: 1px solid oklch(36% 0.05 82 / 0.54);
714+
background:
715+
radial-gradient(circle at 92% 8%, color-mix(in oklch, var(--theme-color) 16%, transparent), transparent 5.5rem),
716+
linear-gradient(180deg, var(--bone-high), var(--bone) 64%, var(--bone-deep));
717+
box-shadow:
718+
0 1px 0 oklch(100% 0 0 / 0.66) inset,
719+
0 18px 54px oklch(4% 0.02 85 / 0.42);
720+
color: var(--ink);
721+
transform-origin: right bottom;
722+
animation: language-menu-in 160ms cubic-bezier(0.22, 1, 0.36, 1);
723+
}
724+
725+
.language-menu[hidden] {
726+
display: none;
727+
}
728+
729+
.language-menu button {
730+
position: relative;
731+
min-height: 34px;
732+
width: 100%;
733+
display: grid;
734+
grid-template-columns: minmax(0, 1fr) auto;
735+
align-items: center;
736+
gap: 10px;
737+
padding: 0 30px 0 10px;
738+
border: 1px solid transparent;
739+
border-radius: 2px;
740+
background: transparent;
741+
color: var(--ink-soft);
742+
font-family: var(--font-mono);
743+
font-weight: 800;
744+
line-height: 1;
745+
text-align: left;
746+
transition:
747+
background-color 160ms ease,
748+
border-color 160ms ease,
749+
color 160ms ease,
750+
transform 160ms ease;
751+
}
752+
753+
.language-menu button:hover,
754+
.language-menu button:focus-visible {
755+
border-color: color-mix(in oklch, var(--theme-color) 38%, var(--line-soft));
756+
background: color-mix(in oklch, var(--theme-color) 10%, transparent);
757+
color: var(--ink);
758+
outline: none;
759+
}
760+
761+
.language-menu button[aria-checked="true"] {
762+
border-color: color-mix(in oklch, var(--theme-color) 48%, var(--line-soft));
763+
background: color-mix(in oklch, var(--theme-color) 16%, var(--bone-high));
764+
color: var(--ink);
765+
}
766+
767+
.language-menu button[aria-checked="true"]::after {
768+
content: "";
769+
position: absolute;
770+
right: 10px;
771+
width: 7px;
772+
height: 7px;
773+
border-radius: 999px;
774+
background: var(--theme-color);
775+
box-shadow: 0 0 0 3px color-mix(in oklch, var(--theme-color) 18%, transparent);
776+
}
777+
778+
.language-menu-name {
779+
overflow: hidden;
780+
font-size: 0.66rem;
781+
letter-spacing: 0.02em;
782+
text-overflow: ellipsis;
783+
white-space: nowrap;
784+
}
785+
786+
.language-menu-code {
787+
font-size: 0.52rem;
788+
letter-spacing: 0.04em;
789+
opacity: 0.62;
790+
text-transform: uppercase;
791+
}
792+
644793
.minimize-btn {
645794
position: absolute;
646795
top: 50%;
@@ -1186,12 +1335,36 @@ figure {
11861335
}
11871336

11881337
.archive-slip {
1338+
position: relative;
1339+
z-index: 3;
1340+
}
1341+
1342+
.strip-actions {
11891343
grid-column: 2;
11901344
grid-row: 1;
1191-
align-self: center;
1345+
display: flex;
1346+
align-items: center;
11921347
justify-self: end;
1193-
position: relative;
1194-
z-index: 3;
1348+
gap: 8px;
1349+
min-width: 0;
1350+
}
1351+
1352+
.specimen-strip .language-switcher {
1353+
margin-left: 0;
1354+
}
1355+
1356+
.specimen-strip .language-menu-button {
1357+
border-color: oklch(94% 0.03 88 / 0.22);
1358+
background:
1359+
linear-gradient(180deg, oklch(94% 0.03 88 / 0.08), oklch(94% 0.03 88 / 0.03));
1360+
color: oklch(94% 0.03 88 / 0.68);
1361+
}
1362+
1363+
.specimen-strip .language-menu-button:hover,
1364+
.specimen-strip .language-switcher.is-open .language-menu-button {
1365+
border-color: color-mix(in oklch, var(--theme-color) 62%, oklch(94% 0.03 88 / 0.26));
1366+
background: color-mix(in oklch, var(--theme-color) 14%, transparent);
1367+
color: var(--bone);
11951368
}
11961369

11971370
.archive-slip summary {
@@ -1457,6 +1630,18 @@ figure {
14571630
}
14581631
}
14591632

1633+
@keyframes language-menu-in {
1634+
from {
1635+
opacity: 0;
1636+
transform: translate3d(0, 6px, 0) scale(0.98);
1637+
}
1638+
1639+
to {
1640+
opacity: 1;
1641+
transform: translate3d(0, 0, 0) scale(1);
1642+
}
1643+
}
1644+
14601645
::view-transition-group(player) {
14611646
animation-duration: 720ms;
14621647
animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
@@ -1772,6 +1957,21 @@ body.player-is-minimized .keyboard-hints {
17721957
font-size: 0.56rem;
17731958
}
17741959

1960+
.stamp-rule-top {
1961+
display: grid;
1962+
grid-template-columns: minmax(0, 1fr) auto;
1963+
align-items: center;
1964+
padding-right: 42px;
1965+
}
1966+
1967+
.stamp-rule-top .eyebrow {
1968+
grid-column: 1;
1969+
}
1970+
1971+
.volume-mark {
1972+
grid-column: 2;
1973+
}
1974+
17751975
.track-title {
17761976
font-size: 2rem;
17771977
}
@@ -1847,16 +2047,66 @@ body.player-is-minimized .keyboard-hints {
18472047
}
18482048

18492049
.specimen-strip {
1850-
grid-template-rows: 38px minmax(0, 1fr);
2050+
grid-template-rows: 44px minmax(0, 1fr);
18512051
gap: 0;
18522052
padding: 2px 10px calc(4px + env(safe-area-inset-bottom));
18532053
}
18542054

18552055
.archive-slip summary {
18562056
justify-content: center;
1857-
width: 38px;
1858-
min-height: 32px;
2057+
width: 40px;
2058+
min-height: 40px;
2059+
padding: 0;
2060+
}
2061+
2062+
.strip-actions {
2063+
gap: 6px;
2064+
}
2065+
2066+
.specimen-strip .language-switcher {
2067+
position: fixed;
2068+
top: max(14px, calc(env(safe-area-inset-top) + 14px));
2069+
right: max(14px, calc(env(safe-area-inset-right) + 14px));
2070+
z-index: 28;
2071+
}
2072+
2073+
.specimen-strip .language-menu-button {
2074+
width: 40px;
2075+
min-width: 40px;
2076+
min-height: 40px;
18592077
padding: 0;
2078+
border-color: var(--line-soft);
2079+
background:
2080+
radial-gradient(circle at 35% 18%, oklch(100% 0 0 / 0.55), transparent 58%),
2081+
linear-gradient(180deg, var(--bone-high), var(--bone) 70%, var(--bone-deep));
2082+
color: var(--ink-soft);
2083+
box-shadow:
2084+
0 1px 0 oklch(100% 0 0 / 0.56) inset,
2085+
0 8px 18px oklch(4% 0.02 85 / 0.18);
2086+
}
2087+
2088+
.specimen-strip .language-menu-button:hover,
2089+
.specimen-strip .language-switcher.is-open .language-menu-button {
2090+
border-color: color-mix(in oklch, var(--theme-color) 50%, var(--line));
2091+
background: color-mix(in oklch, var(--theme-color) 14%, var(--bone-high));
2092+
color: var(--ink);
2093+
}
2094+
2095+
.language-icon {
2096+
width: 18px;
2097+
height: 18px;
2098+
}
2099+
2100+
.language-current-label {
2101+
display: none;
2102+
}
2103+
2104+
.language-menu {
2105+
top: calc(100% + 8px);
2106+
right: 0;
2107+
bottom: auto;
2108+
width: min(188px, calc(100vw - 20px));
2109+
transform-origin: right top;
18602110
}
18612111

18622112
.archive-slip summary span:last-child {

0 commit comments

Comments
 (0)