@hanology/cham-browser 0.4.28 → 0.4.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hanology/cham-browser",
3
- "version": "0.4.28",
3
+ "version": "0.4.29",
4
4
  "description": "CHAM — browser-compatible parser, serializer, and site generator for Classical Han Annotated Markdown",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -48,18 +48,20 @@ onUnmounted(() => window.removeEventListener('scroll', onScroll))
48
48
  align-items: center;
49
49
  justify-content: center;
50
50
  box-shadow: 0 4px 16px rgba(var(--shadow-rgb), 0.1);
51
- transition: all 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
51
+ transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
52
+ backdrop-filter: blur(8px);
53
+ -webkit-backdrop-filter: blur(8px);
52
54
  }
53
55
 
54
56
  @media (max-width: 768px) {
55
57
  .btt { bottom: 88px; right: 16px; width: 36px; height: 36px; }
56
58
  }
57
59
  .btt:hover {
58
- background: var(--ink);
59
- color: var(--paper);
60
- border-color: var(--ink);
61
- transform: translateY(-2px);
62
- box-shadow: 0 8px 24px rgba(var(--shadow-rgb), 0.16);
60
+ background: var(--vermillion);
61
+ color: #fff;
62
+ border-color: var(--vermillion);
63
+ transform: translateY(-3px);
64
+ box-shadow: 0 8px 24px rgba(194, 58, 43, 0.2);
63
65
  }
64
66
 
65
67
  .btt-enter-active { transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); }
@@ -44,7 +44,8 @@ function onTap(event: MouseEvent) {
44
44
  <div
45
45
  v-for="(_, i) in verses"
46
46
  :key="i"
47
- class="h-display-line"
47
+ class="h-display-line h-verse-anim"
48
+ :style="{ animationDelay: (0.15 + i * 0.08) + 's' }"
48
49
  v-html="verseHtml(i)"
49
50
  />
50
51
  </div>
@@ -59,6 +60,7 @@ function onTap(event: MouseEvent) {
59
60
  padding: 40px 56px;
60
61
  box-shadow: 0 4px 16px rgba(var(--shadow-rgb), 0.08);
61
62
  text-align: center;
63
+ animation: poemReveal 0.5s var(--ease-out-expo, cubic-bezier(0.16, 1, 0.3, 1)) both;
62
64
  }
63
65
  .h-display-title {
64
66
  font-size: 32px; font-weight: 900;
@@ -73,18 +75,30 @@ function onTap(event: MouseEvent) {
73
75
  font-size: var(--main-font-size, 24px); line-height: 2.6;
74
76
  letter-spacing: 4px; color: var(--ink);
75
77
  }
78
+ .h-verse-anim {
79
+ animation: verseFade 0.45s var(--ease-out-expo, cubic-bezier(0.16, 1, 0.3, 1)) both;
80
+ }
81
+ @keyframes poemReveal {
82
+ from { opacity: 0; transform: translateY(16px) scale(0.98); }
83
+ to { opacity: 1; transform: translateY(0) scale(1); }
84
+ }
85
+ @keyframes verseFade {
86
+ from { opacity: 0; transform: translateY(8px); }
87
+ to { opacity: 1; transform: translateY(0); }
88
+ }
76
89
 
77
90
  :deep(.ann-target) {
78
91
  border-bottom: 2px solid var(--vermillion);
79
92
  cursor: help;
80
- transition: background 0.15s;
93
+ transition: background 0.2s ease, box-shadow 0.2s ease;
81
94
  }
82
95
  :deep(.ann-target.ann-overlap) {
83
96
  border-bottom-width: 3px;
84
97
  border-bottom-style: double;
85
98
  }
86
99
  :deep(.ann-target:hover) {
87
- background: rgba(194, 58, 43, 0.08);
100
+ background: rgba(194, 58, 43, 0.1);
101
+ box-shadow: 0 2px 8px rgba(194, 58, 43, 0.08);
88
102
  }
89
103
  :deep(.ann-num) {
90
104
  font-size: 10px;
@@ -96,7 +110,8 @@ function onTap(event: MouseEvent) {
96
110
  letter-spacing: 0;
97
111
  }
98
112
  :deep(.ann-target.pronunciation:hover) {
99
- background: rgba(58, 107, 94, 0.08);
113
+ background: rgba(58, 107, 94, 0.1);
114
+ box-shadow: 0 2px 8px rgba(58, 107, 94, 0.08);
100
115
  }
101
116
  :deep(.ann-target.pronunciation) {
102
117
  border-bottom-color: var(--jade);
@@ -117,19 +132,24 @@ function onTap(event: MouseEvent) {
117
132
  border-bottom-color: var(--ann-allusion);
118
133
  }
119
134
  :deep(.ann-target.person:hover) {
120
- background: rgba(58, 90, 140, 0.08);
135
+ background: rgba(58, 90, 140, 0.1);
136
+ box-shadow: 0 2px 8px rgba(58, 90, 140, 0.08);
121
137
  }
122
138
  :deep(.ann-target.place:hover) {
123
- background: rgba(139, 105, 20, 0.08);
139
+ background: rgba(139, 105, 20, 0.1);
140
+ box-shadow: 0 2px 8px rgba(139, 105, 20, 0.08);
124
141
  }
125
142
  :deep(.ann-target.event:hover) {
126
- background: rgba(107, 76, 138, 0.08);
143
+ background: rgba(107, 76, 138, 0.1);
144
+ box-shadow: 0 2px 8px rgba(107, 76, 138, 0.08);
127
145
  }
128
146
  :deep(.ann-target.date:hover) {
129
- background: rgba(42, 122, 122, 0.08);
147
+ background: rgba(42, 122, 122, 0.1);
148
+ box-shadow: 0 2px 8px rgba(42, 122, 122, 0.08);
130
149
  }
131
150
  :deep(.ann-target.allusion:hover) {
132
- background: rgba(181, 101, 29, 0.08);
151
+ background: rgba(181, 101, 29, 0.1);
152
+ box-shadow: 0 2px 8px rgba(181, 101, 29, 0.08);
133
153
  }
134
154
 
135
155
  @media (max-width: 768px) {
@@ -40,15 +40,16 @@ const preview = computed(() => {
40
40
  position: absolute;
41
41
  top: 0; left: 0;
42
42
  width: 3px; height: 0;
43
- background: var(--vermillion);
44
- transition: height 0.3s var(--ease-out-expo, cubic-bezier(0.16, 1, 0.3, 1));
43
+ background: linear-gradient(180deg, var(--vermillion), var(--gold));
44
+ transition: height 0.35s var(--ease-out-expo, cubic-bezier(0.16, 1, 0.3, 1));
45
45
  }
46
46
  .pc-root:hover {
47
- transform: translateY(-2px);
48
- box-shadow: 0 8px 32px rgba(var(--shadow-rgb), 0.08);
47
+ transform: translateY(-3px);
48
+ box-shadow: 0 8px 28px rgba(var(--shadow-rgb), 0.1);
49
49
  border-color: var(--gold);
50
50
  }
51
51
  .pc-root:hover .pc-accent { height: 100%; }
52
+ .pc-root:hover .pc-title { color: var(--vermillion); }
52
53
  .pc-root:active { transform: scale(0.98); }
53
54
  .pc-body { padding: 24px; }
54
55
  .pc-num {
@@ -67,6 +68,7 @@ const preview = computed(() => {
67
68
  font-size: 20px; font-weight: 700;
68
69
  letter-spacing: 2px; margin-bottom: 6px;
69
70
  color: var(--ink);
71
+ transition: color 0.25s ease;
70
72
  }
71
73
  .pc-author {
72
74
  font-size: 13px; color: var(--ink-light);
@@ -130,6 +132,7 @@ const preview = computed(() => {
130
132
  .pc-vertical .pc-accent {
131
133
  top: auto; left: 0; bottom: 0;
132
134
  width: 0; height: 3px;
135
+ background: linear-gradient(90deg, var(--gold), var(--vermillion));
133
136
  transition: width 0.35s ease;
134
137
  }
135
138
  .pc-vertical:hover {
@@ -69,18 +69,18 @@ onUnmounted(detach)
69
69
  z-index: 1001;
70
70
  pointer-events: none;
71
71
  will-change: width, height;
72
- transition: width 0.1s ease, height 0.1s ease;
72
+ transition: width 0.08s linear, height 0.08s linear;
73
73
  }
74
74
  .rp:not(.rp-v) {
75
75
  top: 0; left: 0;
76
- height: 2px;
77
- background: var(--vermillion);
78
- box-shadow: 0 0 8px rgba(194, 58, 43, 0.3);
76
+ height: 3px;
77
+ background: linear-gradient(90deg, var(--vermillion), var(--gold));
78
+ box-shadow: 0 0 8px rgba(194, 58, 43, 0.2);
79
79
  }
80
80
  .rp-v {
81
81
  top: 0; left: 0;
82
- width: 2px;
83
- background: var(--vermillion);
84
- box-shadow: 0 0 8px rgba(194, 58, 43, 0.3);
82
+ width: 3px;
83
+ background: linear-gradient(180deg, var(--vermillion), var(--gold));
84
+ box-shadow: 0 0 8px rgba(194, 58, 43, 0.2);
85
85
  }
86
86
  </style>
@@ -89,8 +89,8 @@ const paragraphsHtml = computed(() => {
89
89
  .sb-root {
90
90
  margin-bottom: 40px;
91
91
  opacity: 0;
92
- transform: translateY(12px);
93
- transition: opacity 0.5s ease, transform 0.5s var(--ease-out-expo, cubic-bezier(0.16, 1, 0.3, 1));
92
+ transform: translateY(16px);
93
+ transition: opacity 0.6s ease, transform 0.6s var(--ease-out-expo, cubic-bezier(0.16, 1, 0.3, 1));
94
94
  }
95
95
  .sb-root.sb-visible {
96
96
  opacity: 1;
@@ -47,7 +47,8 @@ function onTap(event: MouseEvent) {
47
47
  <span
48
48
  v-for="(_, i) in verses"
49
49
  :key="i"
50
- class="v-scroll-line"
50
+ class="v-scroll-line v-verse-anim"
51
+ :style="{ animationDelay: (0.2 + i * 0.06) + 's' }"
51
52
  v-html="verseHtml(i)"
52
53
  />
53
54
  </div>
@@ -68,6 +69,7 @@ function onTap(event: MouseEvent) {
68
69
  position: relative;
69
70
  scrollbar-width: thin;
70
71
  scrollbar-color: var(--gold) transparent;
72
+ animation: poemRevealV 0.5s var(--ease-out-expo, cubic-bezier(0.16, 1, 0.3, 1)) both;
71
73
  }
72
74
  .v-scroll::-webkit-scrollbar { height: 3px; }
73
75
  .v-scroll::-webkit-scrollbar-thumb { background: var(--gold); border-radius: 2px; }
@@ -89,12 +91,23 @@ function onTap(event: MouseEvent) {
89
91
  font-size: var(--main-font-size, 24px); line-height: 2.4; letter-spacing: 6px;
90
92
  color: var(--ink); display: block;
91
93
  }
94
+ .v-verse-anim {
95
+ animation: verseFadeV 0.4s var(--ease-out-expo, cubic-bezier(0.16, 1, 0.3, 1)) both;
96
+ }
97
+ @keyframes poemRevealV {
98
+ from { opacity: 0; transform: translateX(12px); }
99
+ to { opacity: 1; transform: translateX(0); }
100
+ }
101
+ @keyframes verseFadeV {
102
+ from { opacity: 0; }
103
+ to { opacity: 1; }
104
+ }
92
105
 
93
106
  :deep(.ann-target) {
94
107
  border-left: 2px solid var(--vermillion);
95
108
  padding-left: 2px;
96
109
  cursor: help;
97
- transition: background 0.15s;
110
+ transition: background 0.2s ease, box-shadow 0.2s ease;
98
111
  }
99
112
  :deep(.ann-target.ann-overlap) {
100
113
  border-left-width: 3px;
@@ -115,10 +128,12 @@ function onTap(event: MouseEvent) {
115
128
  letter-spacing: -1px;
116
129
  }
117
130
  :deep(.ann-target:hover) {
118
- background: rgba(194, 58, 43, 0.08);
131
+ background: rgba(194, 58, 43, 0.1);
132
+ box-shadow: 0 -2px 8px rgba(194, 58, 43, 0.08);
119
133
  }
120
134
  :deep(.ann-target.pronunciation:hover) {
121
- background: rgba(58, 107, 94, 0.08);
135
+ background: rgba(58, 107, 94, 0.1);
136
+ box-shadow: 0 -2px 8px rgba(58, 107, 94, 0.08);
122
137
  }
123
138
  :deep(.ann-target.pronunciation) {
124
139
  border-left-color: var(--jade);
@@ -142,18 +157,23 @@ function onTap(event: MouseEvent) {
142
157
  border-left-color: var(--ann-allusion);
143
158
  }
144
159
  :deep(.ann-target.person:hover) {
145
- background: rgba(58, 90, 140, 0.08);
160
+ background: rgba(58, 90, 140, 0.1);
161
+ box-shadow: 0 -2px 8px rgba(58, 90, 140, 0.08);
146
162
  }
147
163
  :deep(.ann-target.place:hover) {
148
- background: rgba(139, 105, 20, 0.08);
164
+ background: rgba(139, 105, 20, 0.1);
165
+ box-shadow: 0 -2px 8px rgba(139, 105, 20, 0.08);
149
166
  }
150
167
  :deep(.ann-target.event:hover) {
151
- background: rgba(107, 76, 138, 0.08);
168
+ background: rgba(107, 76, 138, 0.1);
169
+ box-shadow: 0 -2px 8px rgba(107, 76, 138, 0.08);
152
170
  }
153
171
  :deep(.ann-target.date:hover) {
154
- background: rgba(42, 122, 122, 0.08);
172
+ background: rgba(42, 122, 122, 0.1);
173
+ box-shadow: 0 -2px 8px rgba(42, 122, 122, 0.08);
155
174
  }
156
175
  :deep(.ann-target.allusion:hover) {
157
- background: rgba(181, 101, 29, 0.08);
176
+ background: rgba(181, 101, 29, 0.1);
177
+ box-shadow: 0 -2px 8px rgba(181, 101, 29, 0.08);
158
178
  }
159
179
  </style>
@@ -141,6 +141,23 @@ html[dir="rtl"] ::-webkit-scrollbar-thumb { background: var(--gold); }
141
141
  a { color: inherit; text-decoration: none; }
142
142
  button { font-family: inherit; }
143
143
 
144
+ /* ===== FOCUS STYLES ===== */
145
+ :focus-visible {
146
+ outline: 2px solid var(--vermillion);
147
+ outline-offset: 2px;
148
+ }
149
+ button:focus-visible {
150
+ outline: 2px solid var(--vermillion);
151
+ outline-offset: 2px;
152
+ border-radius: 4px;
153
+ }
154
+
155
+ /* ===== ACTIVE ANNOTATION FLASH ===== */
156
+ @keyframes ann-flash-anim {
157
+ 0% { background: rgba(194, 58, 43, 0.25); box-shadow: 0 0 12px rgba(194, 58, 43, 0.15); }
158
+ 100% { background: transparent; box-shadow: none; }
159
+ }
160
+
144
161
  /* ===== LOADING SCREEN ===== */
145
162
  #app-loading {
146
163
  position: fixed; inset: 0;
@@ -148,10 +165,11 @@ button { font-family: inherit; }
148
165
  display: flex; flex-direction: column;
149
166
  align-items: center; justify-content: center;
150
167
  z-index: 9999;
151
- transition: opacity 0.4s ease;
168
+ transition: opacity 0.5s ease, transform 0.5s ease;
152
169
  }
153
170
  #app-loading.fade-out {
154
171
  opacity: 0;
172
+ transform: scale(1.02);
155
173
  pointer-events: none;
156
174
  }
157
175
  #app-loading .seal {
@@ -354,15 +354,17 @@ function openBook(bookId: string) {
354
354
  from { opacity: 0; transform: translateY(12px); }
355
355
  to { opacity: 1; transform: translateY(0); }
356
356
  }
357
- .lib-card:hover { border-color: var(--gold); box-shadow: 0 4px 20px rgba(var(--shadow-rgb), 0.1); }
357
+ .lib-card:hover { border-color: var(--gold); box-shadow: 0 6px 24px rgba(var(--shadow-rgb), 0.1); transform: translateY(-2px); }
358
+ .lib-card:active { transform: scale(0.98); }
358
359
  .lib-card-accent {
359
360
  position: absolute;
360
361
  top: 0; left: 0;
361
362
  width: 3px; height: 0;
362
- background: var(--vermillion);
363
- transition: height 0.35s ease;
363
+ background: linear-gradient(180deg, var(--vermillion), var(--gold));
364
+ transition: height 0.35s var(--ease-out-expo, cubic-bezier(0.16, 1, 0.3, 1));
364
365
  }
365
366
  .lib-card:hover .lib-card-accent { height: 100%; }
367
+ .lib-card:hover .lib-card-title { color: var(--vermillion); }
366
368
  .lib-card-top {
367
369
  display: flex;
368
370
  align-items: baseline;
@@ -372,6 +374,7 @@ function openBook(bookId: string) {
372
374
  .lib-card-title {
373
375
  font-size: 22px; font-weight: 900;
374
376
  letter-spacing: 4px; color: var(--ink);
377
+ transition: color 0.25s ease;
375
378
  }
376
379
  .lib-card-genre {
377
380
  font-size: 11px;
@@ -846,13 +846,26 @@ function tcy(n: number): string {
846
846
  border: 1px solid var(--border-light);
847
847
  border-radius: 6px;
848
848
  cursor: pointer;
849
- transition: all 0.2s;
849
+ transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
850
850
  line-height: 1.6;
851
+ position: relative;
852
+ overflow: hidden;
853
+ }
854
+ .v-nav-btn::after {
855
+ content: '';
856
+ position: absolute;
857
+ right: 0; top: 0; bottom: 0;
858
+ width: 2px;
859
+ background: linear-gradient(180deg, var(--gold), var(--vermillion));
860
+ transform: scaleY(0);
861
+ transition: transform 0.35s var(--ease-out-expo, cubic-bezier(0.16, 1, 0.3, 1));
851
862
  }
852
863
  .v-nav-btn:hover {
853
864
  border-color: var(--gold);
854
- box-shadow: 0 4px 16px rgba(var(--shadow-rgb), 0.08);
865
+ box-shadow: 0 4px 20px rgba(var(--shadow-rgb), 0.1);
855
866
  }
867
+ .v-nav-btn:hover::after { transform: scaleY(1); }
868
+ .v-nav-btn:hover .v-nav-title { color: var(--vermillion); }
856
869
  .v-nav-dir {
857
870
  font-size: 16px; color: var(--vermillion);
858
871
  margin-bottom: 0.5em;
@@ -865,6 +878,7 @@ function tcy(n: number): string {
865
878
  .v-nav-title {
866
879
  font-size: 18px; font-weight: 700;
867
880
  letter-spacing: 3px; color: var(--ink);
881
+ transition: color 0.25s ease;
868
882
  }
869
883
 
870
884
  /* ═══════ 橫排模式 ═══════ */
@@ -950,7 +964,7 @@ function tcy(n: number): string {
950
964
  .h-nav-arrow {
951
965
  width: 32px; height: 32px;
952
966
  border: 1px solid var(--border);
953
- border-radius: 4px;
967
+ border-radius: 6px;
954
968
  background: none;
955
969
  font-family: var(--sans);
956
970
  font-size: 16px;
@@ -1026,20 +1040,21 @@ function tcy(n: number): string {
1026
1040
  position: absolute;
1027
1041
  bottom: 0; left: 0; right: 0;
1028
1042
  height: 2px;
1029
- background: var(--vermillion);
1043
+ background: linear-gradient(90deg, var(--vermillion), var(--gold));
1030
1044
  transform: scaleX(0);
1031
- transition: transform 0.3s ease;
1045
+ transition: transform 0.35s var(--ease-out-expo, cubic-bezier(0.16, 1, 0.3, 1));
1032
1046
  }
1033
1047
  .h-nav-btn:hover {
1034
1048
  border-color: var(--gold);
1035
1049
  box-shadow: 0 8px 32px rgba(var(--shadow-rgb), 0.1);
1036
- transform: translateY(-2px);
1050
+ transform: translateY(-3px);
1037
1051
  }
1038
1052
  .h-nav-btn:hover::after { transform: scaleX(1); }
1053
+ .h-nav-btn:hover .h-nav-title { color: var(--vermillion); }
1039
1054
  .h-nav-btn:active { transform: scale(0.98); }
1040
1055
  .h-nav-btn.h-nav-next { text-align: right; }
1041
1056
  .h-nav-label { font-size: 11px; color: var(--ink-faint); font-family: var(--sans); letter-spacing: 2px; margin-bottom: 4px; }
1042
- .h-nav-title { font-size: 16px; font-weight: 600; letter-spacing: 1px; color: var(--ink); }
1057
+ .h-nav-title { font-size: 16px; font-weight: 600; letter-spacing: 1px; color: var(--ink); transition: color 0.25s ease; }
1043
1058
 
1044
1059
  .h-overlay {
1045
1060
  position: fixed; inset: 0;
@@ -1335,11 +1350,11 @@ function tcy(n: number): string {
1335
1350
 
1336
1351
  /* ─── 注釋閃爍 ─── */
1337
1352
  :deep(.ann-flash) {
1338
- animation: ann-flash-anim 1.2s ease-out;
1353
+ animation: ann-flash-anim 1.5s ease-out;
1339
1354
  }
1340
1355
  @keyframes ann-flash-anim {
1341
- 0% { background: rgba(194, 58, 43, 0.2); }
1342
- 100% { background: transparent; }
1356
+ 0% { background: rgba(194, 58, 43, 0.25); box-shadow: 0 0 12px rgba(194, 58, 43, 0.15); }
1357
+ 100% { background: transparent; box-shadow: none; }
1343
1358
  }
1344
1359
 
1345
1360
  /* ═══════ 行動裝置適配 ═══════ */