@liftkit-vue/core 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/LICENSE +21 -0
  2. package/package.json +11 -7
  3. package/src/components/LkBadge.vue +64 -0
  4. package/src/components/LkButton.vue +102 -0
  5. package/src/components/LkCard.vue +85 -0
  6. package/src/components/LkColumn.vue +40 -0
  7. package/src/components/LkContainer.vue +26 -0
  8. package/src/components/LkDropdown/LkDropdown.vue +14 -0
  9. package/src/components/LkDropdown/LkDropdownMenu.vue +99 -0
  10. package/src/components/LkDropdown/LkDropdownTrigger.vue +39 -0
  11. package/src/components/LkDropdown/index.ts +3 -0
  12. package/src/components/LkGrid.vue +58 -0
  13. package/src/components/LkHeading.vue +43 -0
  14. package/src/components/LkIcon.vue +56 -0
  15. package/src/components/LkIconButton.vue +78 -0
  16. package/src/components/LkImage.vue +62 -0
  17. package/src/components/LkMaterialLayer.vue +147 -0
  18. package/src/components/LkMenuItem.vue +51 -0
  19. package/src/components/LkNavbar.vue +113 -0
  20. package/src/components/LkPlaceholderBlock.vue +9 -0
  21. package/src/components/LkRow.vue +40 -0
  22. package/src/components/LkSection.vue +45 -0
  23. package/src/components/LkSelect.vue +167 -0
  24. package/src/components/LkSelectMenu.vue +67 -0
  25. package/src/components/LkSelectOption.vue +56 -0
  26. package/src/components/LkSelectTrigger.vue +43 -0
  27. package/src/components/LkSnackbar.vue +127 -0
  28. package/src/components/LkStateLayer.vue +33 -0
  29. package/src/components/LkSticker.vue +42 -0
  30. package/src/components/LkSwitch.vue +90 -0
  31. package/src/components/LkTabContent.vue +33 -0
  32. package/src/components/LkTabLink.vue +50 -0
  33. package/src/components/LkTabMenu.vue +59 -0
  34. package/src/components/LkTabs.vue +65 -0
  35. package/src/components/LkText.vue +37 -0
  36. package/src/components/LkTextInput.vue +105 -0
  37. package/src/components/LkTheme.vue +49 -0
  38. package/src/components/LkThemeController.vue +396 -0
  39. package/src/css/index.css +3 -1
  40. package/src/css/liftkit-core.css +5 -464
  41. package/src/css/liftkit-reset.css +74 -0
  42. package/src/css/liftkit-tokens.css +375 -0
@@ -0,0 +1,396 @@
1
+ <script setup lang="ts">
2
+ import { ref, computed, watch } from 'vue'
3
+ import { Teleport } from 'vue'
4
+ import { useTheme, type PaletteState } from '../composables'
5
+
6
+ type LkColorGroup =
7
+ | 'master'
8
+ | 'primary'
9
+ | 'secondary'
10
+ | 'tertiary'
11
+ | 'neutral'
12
+ | 'neutralvariant'
13
+ | 'error'
14
+ | 'warning'
15
+ | 'success'
16
+ | 'info'
17
+
18
+ const { palette, updateTheme, updateThemeFromMaster, colorMode, colorModePreference, setColorMode } = useTheme()
19
+
20
+ const brandPalette: LkColorGroup[] = ['primary', 'secondary', 'tertiary']
21
+ const semanticPalette: LkColorGroup[] = ['error', 'warning', 'success', 'info']
22
+ const layoutPalette: LkColorGroup[] = ['neutral', 'neutralvariant']
23
+
24
+ const paletteArray = computed(() =>
25
+ Object.keys(palette.value).map((key) => ({
26
+ key,
27
+ value: palette.value[key],
28
+ }))
29
+ )
30
+
31
+ const isOpen = ref(false)
32
+
33
+ function handleColorChange(key: LkColorGroup, newValue: string) {
34
+ if (key === 'master') {
35
+ updateThemeFromMaster(newValue)
36
+ } else {
37
+ palette.value = {
38
+ ...palette.value,
39
+ [key]: newValue,
40
+ }
41
+ }
42
+ }
43
+
44
+ function handleColorModeChange(mode: 'auto' | 'light' | 'dark') {
45
+ setColorMode(mode)
46
+ }
47
+
48
+ async function handleCopyPalette() {
49
+ try {
50
+ const codeContent = `const palette = ${JSON.stringify(palette.value, null, 2)}\nconst colorMode = '${colorMode.value}'`
51
+ await navigator.clipboard.writeText(codeContent)
52
+ alert('Code copied')
53
+ } catch (err) {
54
+ console.error('Failed to copy palette:', err)
55
+ }
56
+ }
57
+
58
+ function getColorDescription(group: LkColorGroup): string {
59
+ const descriptions: Record<string, string> = {
60
+ primary: 'Main brand color, used for most UI elements.',
61
+ secondary: 'Desaturated variant of primary.',
62
+ tertiary: 'Your accent color. Defaults to complementary hue to primary.',
63
+ error: 'A pink or red, indicating problems.',
64
+ warning: 'An orange or yellow, indicating caution.',
65
+ success: 'A green, indicating success.',
66
+ info: 'A blue, indicating neutral information.',
67
+ neutral: 'Backgrounds, surfaces, outlines, and default text color',
68
+ neutralvariant: 'Surface variant, outline variant, and text color variant',
69
+ }
70
+ return descriptions[group] || ''
71
+ }
72
+ </script>
73
+
74
+ <template>
75
+ <div v-if="!isOpen" class="position-fixed" :style="{ zIndex: 1000 }">
76
+ <button
77
+ class="lk-theme-controller-trigger shadow-xl"
78
+ :style="{
79
+ position: 'fixed',
80
+ zIndex: 1000,
81
+ top: 'var(--lk-size-xs)',
82
+ left: 'var(--lk-size-xs)',
83
+ }"
84
+ @click="isOpen = true"
85
+ >
86
+ <svg xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="2 2 20 20" fill="currentColor"><path d="M12 2C6.49 2 2 6.49 2 12s4.49 10 10 10a2.5 2.5 0 0 0 2.5-2.5c0-.61-.23-1.21-.64-1.67a.528.528 0 0 1-.13-.33c0-.28.22-.5.5-.5H16c3.31 0 6-2.69 6-6 0-4.96-4.49-9-10-9m-5.5 9a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3m3-4a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3m5 0a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3m3 4a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3"/></svg>
87
+ </button>
88
+ </div>
89
+
90
+ <Teleport to="body">
91
+ <div v-if="isOpen" class="lk-theme-drawer">
92
+ <div class="lk-theme-drawer-card shadow-lg mb-2xl">
93
+ <div class="lk-theme-drawer-scroll">
94
+ <div class="lk-column" style="gap: var(--lk-size-md)">
95
+ <!-- Header -->
96
+ <div class="lk-row" style="align-items: center; justify-content: space-between">
97
+ <h2 class="body-bold">Theme Controller</h2>
98
+ <button
99
+ :style="{
100
+ display: 'inline-flex',
101
+ alignItems: 'center',
102
+ justifyContent: 'center',
103
+ background: '#e8e0e5',
104
+ border: 'none',
105
+ borderRadius: '50%',
106
+ width: '36px',
107
+ height: '36px',
108
+ cursor: 'pointer',
109
+ padding: '0',
110
+ }"
111
+ @click="isOpen = false"
112
+ aria-label="Close"
113
+ >
114
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" style="display: block"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" fill="#1b1b1f"/></svg>
115
+ </button>
116
+ </div>
117
+
118
+ <!-- Config export -->
119
+ <div>
120
+ <h2 class="label mb-xs">Config</h2>
121
+ <p class="caption color-onsurfacevariant mb-xs">
122
+ Copy and paste this snippet into your theme config to update your project to match
123
+ the current configuration.
124
+ </p>
125
+ <div class="lk-theme-code-block position-relative">
126
+ <pre style="font-size: 0.618em; overflow: auto">
127
+ const palette = {{ JSON.stringify(palette, null, 2) }}
128
+ const colorMode = '{{ colorMode }}'</pre
129
+ >
130
+ <button
131
+ :style="{
132
+ display: 'inline-flex',
133
+ alignItems: 'center',
134
+ justifyContent: 'center',
135
+ background: '#e8e0e5',
136
+ border: 'none',
137
+ borderRadius: '50%',
138
+ width: '36px',
139
+ height: '36px',
140
+ cursor: 'pointer',
141
+ padding: '0',
142
+ position: 'absolute',
143
+ top: '1em',
144
+ right: '1em',
145
+ }"
146
+ @click="handleCopyPalette"
147
+ >
148
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" style="display: block"><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z" fill="#1b1b1f"/></svg>
149
+ </button>
150
+ </div>
151
+ </div>
152
+
153
+ <!-- Color Mode -->
154
+ <div>
155
+ <h2 class="capline mb-lg color-onsurfacevariant">Mode</h2>
156
+ <p class="caption color-onsurfacevariant mb-xs">
157
+ Current: {{ colorMode }} {{ colorModePreference === 'auto' ? '(auto)' : '' }}
158
+ </p>
159
+ <div class="lk-row" style="gap: var(--lk-size-xs)">
160
+ <button
161
+ v-for="mode in (['auto', 'light', 'dark'] as const)"
162
+ :key="mode"
163
+ class="lk-mode-button"
164
+ :class="{ active: colorModePreference === mode }"
165
+ @click="handleColorModeChange(mode)"
166
+ >
167
+ {{ mode }}
168
+ </button>
169
+ </div>
170
+ </div>
171
+
172
+ <!-- Master color -->
173
+ <div>
174
+ <h2 class="capline mb-lg color-onsurfacevariant">Globals</h2>
175
+ <div class="lk-row" style="align-items: start; gap: var(--lk-size-md)">
176
+ <input
177
+ type="color"
178
+ name="master"
179
+ class="lk-color-input"
180
+ @input="($event) => handleColorChange('master', ($event.target as HTMLInputElement).value)"
181
+ />
182
+ <div class="lk-column">
183
+ <label class="label mb-xs" for="master">master</label>
184
+ <p class="caption color-onsurfacevariant mb-xs">
185
+ The seed color.
186
+ <strong class="color-error"
187
+ >If you edit this, all other color tokens will reset.</strong
188
+ >
189
+ </p>
190
+ </div>
191
+ </div>
192
+ </div>
193
+
194
+ <!-- Brand colors -->
195
+ <div>
196
+ <h2 class="capline mb-lg color-onsurfacevariant">Brand</h2>
197
+ <div
198
+ v-for="colorGroup in brandPalette"
199
+ :key="colorGroup"
200
+ class="lk-row mb-sm"
201
+ style="align-items: start; gap: var(--lk-size-md)"
202
+ >
203
+ <input
204
+ type="color"
205
+ :name="colorGroup"
206
+ :value="palette[colorGroup]"
207
+ class="lk-color-input"
208
+ @input="($event) => handleColorChange(colorGroup, ($event.target as HTMLInputElement).value)"
209
+ />
210
+ <div class="lk-column">
211
+ <label class="caption-bold mono mb-2xs" :for="colorGroup">{{
212
+ colorGroup
213
+ }}</label>
214
+ <p class="caption color-onsurfacevariant mb-xs">
215
+ {{ getColorDescription(colorGroup) }}
216
+ </p>
217
+ </div>
218
+ </div>
219
+ </div>
220
+
221
+ <!-- Semantic colors -->
222
+ <div>
223
+ <h2 class="capline color-onsurfacevariant mb-lg">Semantic</h2>
224
+ <div
225
+ v-for="colorGroup in semanticPalette"
226
+ :key="colorGroup"
227
+ class="lk-row mb-sm"
228
+ style="align-items: start; gap: var(--lk-size-md)"
229
+ >
230
+ <input
231
+ type="color"
232
+ :name="colorGroup"
233
+ :value="palette[colorGroup]"
234
+ class="lk-color-input"
235
+ @input="($event) => handleColorChange(colorGroup, ($event.target as HTMLInputElement).value)"
236
+ />
237
+ <div class="lk-column">
238
+ <label class="caption-bold mono mb-2xs" :for="colorGroup">{{
239
+ colorGroup
240
+ }}</label>
241
+ <p class="caption color-onsurfacevariant mb-xs">
242
+ {{ getColorDescription(colorGroup) }}
243
+ </p>
244
+ </div>
245
+ </div>
246
+ </div>
247
+
248
+ <!-- Layout colors -->
249
+ <div>
250
+ <h2 class="capline color-onsurfacevariant mb-lg">Layout</h2>
251
+ <div
252
+ v-for="colorGroup in layoutPalette"
253
+ :key="colorGroup"
254
+ class="lk-row mb-sm"
255
+ style="align-items: start; gap: var(--lk-size-md)"
256
+ >
257
+ <input
258
+ type="color"
259
+ :name="colorGroup"
260
+ :value="palette[colorGroup]"
261
+ class="lk-color-input"
262
+ @input="($event) => handleColorChange(colorGroup, ($event.target as HTMLInputElement).value)"
263
+ />
264
+ <div class="lk-column">
265
+ <label class="caption-bold mono mb-xs" :for="colorGroup">{{
266
+ colorGroup
267
+ }}</label>
268
+ <p class="caption color-onsurfacevariant mb-xs">
269
+ {{ getColorDescription(colorGroup) }}
270
+ </p>
271
+ </div>
272
+ </div>
273
+ </div>
274
+ </div>
275
+ </div>
276
+ </div>
277
+ </div>
278
+ </Teleport>
279
+ </template>
280
+
281
+ <style scoped>
282
+ .lk-theme-controller-trigger {
283
+ display: flex;
284
+ align-items: center;
285
+ justify-content: center;
286
+ background: var(--light__inversesurface_lkv, #303034);
287
+ color: var(--light__inverseonsurface_lkv, #f2f0f4);
288
+ border: none;
289
+ border-radius: 50%;
290
+ width: 48px;
291
+ height: 48px;
292
+ cursor: pointer;
293
+ }
294
+
295
+ .lk-color-input {
296
+ -webkit-appearance: none;
297
+ -moz-appearance: none;
298
+ appearance: none;
299
+ width: 36px;
300
+ height: 36px;
301
+ flex: 0 0 auto;
302
+ background-color: transparent;
303
+ border: 2px solid var(--light__onsurface_lkv, #1b1b1f);
304
+ cursor: pointer;
305
+ outline: none;
306
+ border-radius: 100em;
307
+ padding: 0;
308
+ }
309
+
310
+ .lk-color-input::-webkit-color-swatch-wrapper {
311
+ padding: 0;
312
+ }
313
+
314
+ .lk-color-input::-webkit-color-swatch {
315
+ border-radius: 100em;
316
+ border: none;
317
+ }
318
+
319
+ .lk-color-input::-moz-color-swatch {
320
+ border-radius: 100%;
321
+ border: none;
322
+ }
323
+
324
+ .lk-theme-drawer {
325
+ position: fixed;
326
+ top: 0;
327
+ left: 0;
328
+ bottom: 0;
329
+ overflow: hidden;
330
+ width: calc(var(--lk-size-4xl, 320px) * var(--lk-wholestep, 1.5));
331
+ z-index: 1000;
332
+ padding: var(--lk-size-md, 16px);
333
+ }
334
+
335
+ .lk-theme-drawer-card {
336
+ height: 100%;
337
+ background: var(--light__surfacecontainerlowest_lkv, #ffffff);
338
+ color: var(--light__onsurface_lkv, #1b1b1f);
339
+ border-radius: var(--lk-size-sm, 8px);
340
+ padding: var(--lk-size-md, 16px);
341
+ }
342
+
343
+ .lk-theme-drawer-scroll {
344
+ height: 100%;
345
+ overflow-y: auto;
346
+ }
347
+
348
+ .lk-theme-code-block {
349
+ background: var(--lk-surfacecontainerlowest, #ffffff);
350
+ border-radius: var(--lk-size-xs, 4px);
351
+ padding: var(--lk-size-sm, 8px);
352
+ position: relative;
353
+ }
354
+
355
+ .lk-icon-button {
356
+ display: inline-flex;
357
+ align-items: center;
358
+ justify-content: center;
359
+ background: var(--light__surfacecontainerhigh_lkv, #e8e0e5);
360
+ border: none;
361
+ border-radius: 50%;
362
+ width: 36px;
363
+ height: 36px;
364
+ cursor: pointer;
365
+ color: var(--light__onsurface_lkv, #1b1b1f);
366
+ }
367
+
368
+ .lk-column {
369
+ display: flex;
370
+ flex-direction: column;
371
+ }
372
+
373
+ .lk-row {
374
+ display: flex;
375
+ flex-direction: row;
376
+ }
377
+
378
+ .lk-mode-button {
379
+ flex: 1;
380
+ padding: 6px 12px;
381
+ border: 1px solid var(--light__outline_lkv, #767680);
382
+ border-radius: 100em;
383
+ background: transparent;
384
+ color: var(--light__onsurface_lkv, #1b1b1f);
385
+ cursor: pointer;
386
+ font-size: 0.75rem;
387
+ font-weight: 500;
388
+ text-transform: capitalize;
389
+ }
390
+
391
+ .lk-mode-button.active {
392
+ background: var(--light__primary_lkv, #004ee7);
393
+ color: var(--light__onprimary_lkv, #ffffff);
394
+ border-color: var(--light__primary_lkv, #004ee7);
395
+ }
396
+ </style>
package/src/css/index.css CHANGED
@@ -1,4 +1,6 @@
1
- @import "./liftkit-core.css";
1
+ /* トークン(変数)とリセット(要素セレクタ)を分離インポート */
2
+ @import "./liftkit-tokens.css";
3
+ @import "./liftkit-reset.css";
2
4
  @import "./align-items.css";
3
5
  @import "./align-self.css";
4
6
  @import "./aspect-ratios.css";