Rebuild the UI in an Apple style with spring-driven motion

Replace the look of every page (key, lobby, create, table, range, review)
with light/dark semantic colors, translucent materials, iOS-style grouped
lists and system typography. Add fluid.js, a small spring engine with
interruptible, velocity-aware animations, and use it for draggable bottom
sheets, sliding segmented controls, a custom bet slider with rubber-banding,
swipe-to-delete in the lobby, and dealt/flipped cards. Seats and cards are
now reused between renders so animations are not cut off. Honors reduced
motion, reduced transparency and increased contrast.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
agomizi1cmandClaude Opus 5.5 committed 2026-10-21 21:44:21 +09:00
1 parent d5ef2d310f
commit c3fbfdd12e
18 files changed
+4877 -1278

No files matched your search

+10 -2
View File
@@ -5,6 +5,10 @@
const $ = (id) => document.getElementById(id);
const { RANKS, CATEGORIES, FACING_ACTIONS, handAt, categoryOf, labelOf, facingOf, facingAction } = window.HandRange;
Fluid.navBar($('nav'), $('large-title'));
const tabsSeg = Fluid.Segmented($('tabs'));
Fluid.Segmented($('count'));
function swatch(id) {
const s = document.createElement('span');
s.className = `swatch-dot cat-${id}`;
@@ -22,7 +26,8 @@
function showView(name) {
$('view-range').hidden = name !== 'range';
$('view-quiz').hidden = name !== 'quiz';
for (const b of $('tabs').children) b.classList.toggle('active', b.dataset.view === name);
for (const b of $('tabs').querySelectorAll('[data-view]')) b.classList.toggle('active', b.dataset.view === name);
tabsSeg.update();
}
$('tabs').addEventListener('click', (e) => {
const b = e.target.closest('[data-view]');
@@ -99,7 +104,7 @@
const b = e.target.closest('[data-count]');
if (!b) return;
quizCount = Number(b.dataset.count);
for (const x of $('count').children) x.classList.toggle('active', x === b);
for (const x of $('count').querySelectorAll('[data-count]')) x.classList.toggle('active', x === b);
});
function shuffle(a) {
@@ -186,6 +191,9 @@
fb.className = `quiz-feedback ${ok ? 'good' : 'bad'}`;
fb.textContent = `${ok ? '正解!' : '不正解'} ${hand} は「${labelOf(right)}」です。`;
fb.hidden = false;
// 間違えたら選んだボタンが揺れる(振動と同じ瞬間に)
if (!ok) Fluid.shake(button);
Fluid.haptic(ok ? 10 : [18, 50, 18]);
updateStats();
$('next-question').disabled = false;
}