@troshab/slidev-theme-troshab 0.1.0
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/CLAUDE.md +537 -0
- package/LICENSE +134 -0
- package/README.md +168 -0
- package/SKILL.md +414 -0
- package/components/AnimatedCounter.vue +35 -0
- package/components/Background.vue +204 -0
- package/components/Callout.vue +135 -0
- package/components/Card.vue +75 -0
- package/components/CardGrid.vue +67 -0
- package/components/CaseStudy.vue +66 -0
- package/components/CodeDiff.vue +229 -0
- package/components/CodeHighlight.vue +337 -0
- package/components/ColorSwatch.vue +114 -0
- package/components/Confetti.vue +292 -0
- package/components/Conversation.vue +405 -0
- package/components/Countdown.vue +476 -0
- package/components/Definition.vue +59 -0
- package/components/DeviceMockup.vue +392 -0
- package/components/Funnel.vue +87 -0
- package/components/Icon.vue +73 -0
- package/components/Iframe.vue +38 -0
- package/components/Image.vue +69 -0
- package/components/ImageCompare.vue +436 -0
- package/components/MatrixGrid.vue +85 -0
- package/components/MermaidChart.vue +299 -0
- package/components/Metric.vue +161 -0
- package/components/PersonCard.vue +165 -0
- package/components/PricingTable.vue +144 -0
- package/components/Progress.vue +100 -0
- package/components/Pyramid.vue +81 -0
- package/components/QRCode.vue +137 -0
- package/components/QuoteBlock.vue +101 -0
- package/components/SpeechBubble.vue +169 -0
- package/components/Stepper.vue +542 -0
- package/components/StyledList.vue +156 -0
- package/components/StyledText.vue +275 -0
- package/components/SwotGrid.vue +99 -0
- package/components/Tags.vue +20 -0
- package/components/Testimonial.vue +243 -0
- package/components/Typewriter.vue +181 -0
- package/components_base/AnimatedCounter.vue +208 -0
- package/components_base/CodeHighlight.vue +364 -0
- package/composables/useColors.ts +101 -0
- package/composables/useShiki.ts +81 -0
- package/example_content.md +371 -0
- package/example_dark.md +10 -0
- package/example_slides/001-cover.md +15 -0
- package/example_slides/002-agenda.md +25 -0
- package/example_slides/003-section-layouts.md +14 -0
- package/example_slides/004-fullscreen-centered.md +7 -0
- package/example_slides/005-fullscreen-align-bottom.md +14 -0
- package/example_slides/006-fullscreen-no-padding.md +14 -0
- package/example_slides/007-fullscreen-bg-image-dark.md +13 -0
- package/example_slides/008-fullscreen-bg-image-light.md +13 -0
- package/example_slides/009-fullscreen-bg-gradient.md +15 -0
- package/example_slides/010-fullscreen-bg-color.md +13 -0
- package/example_slides/011-split-basic.md +17 -0
- package/example_slides/012-split-image-text.md +18 -0
- package/example_slides/013-split-contrast.md +22 -0
- package/example_slides/014-columns-basic.md +13 -0
- package/example_slides/015-columns-two.md +26 -0
- package/example_slides/016-columns-ratios.md +22 -0
- package/example_slides/017-columns-three.md +31 -0
- package/example_slides/018-columns-four.md +22 -0
- package/example_slides/019-columns-alignment.md +23 -0
- package/example_slides/020-columns-styled.md +21 -0
- package/example_slides/021-footnote-prop.md +16 -0
- package/example_slides/022-iframe-fullscreen.md +8 -0
- package/example_slides/023-iframe-split.md +18 -0
- package/example_slides/024-section-components.md +14 -0
- package/example_slides/025-styled-text.md +9 -0
- package/example_slides/026-styled-text.md +15 -0
- package/example_slides/027-text-formatting.md +28 -0
- package/example_slides/028-text-spoiler.md +15 -0
- package/example_slides/029-icon-component.md +47 -0
- package/example_slides/030-metric-component.md +29 -0
- package/example_slides/031-person-card.md +33 -0
- package/example_slides/032-styled-list.md +50 -0
- package/example_slides/033-color-swatch.md +35 -0
- package/example_slides/034-code-highlight.md +9 -0
- package/example_slides/035-iframe-component.md +9 -0
- package/example_slides/036-callout.md +15 -0
- package/example_slides/037-card-grid.md +27 -0
- package/example_slides/038-stepper-variants.md +18 -0
- package/example_slides/039-stepper-clicks.md +49 -0
- package/example_slides/040-stepper-interactive.md +28 -0
- package/example_slides/041-tags-progress.md +21 -0
- package/example_slides/042-speech-bubble.md +30 -0
- package/example_slides/043-conversation.md +13 -0
- package/example_slides/044-device-iphone.md +26 -0
- package/example_slides/045-device-browser.md +7 -0
- package/example_slides/046-qrcode.md +26 -0
- package/example_slides/047-countdown.md +14 -0
- package/example_slides/048-typewriter.md +8 -0
- package/example_slides/049-confetti.md +16 -0
- package/example_slides/050-image-compare.md +13 -0
- package/example_slides/051-code-diff.md +24 -0
- package/example_slides/052-quote-block.md +8 -0
- package/example_slides/053-testimonial.md +26 -0
- package/example_slides/054-testimonial-featured.md +16 -0
- package/example_slides/055-funnel.md +12 -0
- package/example_slides/056-pyramid.md +13 -0
- package/example_slides/057-pricing-table.md +9 -0
- package/example_slides/058-swot-grid.md +12 -0
- package/example_slides/059-matrix-grid.md +12 -0
- package/example_slides/060-case-study.md +11 -0
- package/example_slides/061-definition.md +15 -0
- package/example_slides/062-mermaid-intro.md +34 -0
- package/example_slides/063-mermaid-flowchart.md +19 -0
- package/example_slides/064-mermaid-sequence.md +17 -0
- package/example_slides/065-mermaid-xy-chart.md +16 -0
- package/example_slides/066-mermaid-pie.md +17 -0
- package/example_slides/067-mermaid-class.md +19 -0
- package/example_slides/068-mermaid-state.md +19 -0
- package/example_slides/069-mermaid-er.md +22 -0
- package/example_slides/070-mermaid-gantt.md +24 -0
- package/example_slides/071-mermaid-timeline.md +17 -0
- package/example_slides/072-mermaid-mindmap.md +21 -0
- package/example_slides/073-mermaid-gitgraph.md +20 -0
- package/example_slides/074-mermaid-split.md +30 -0
- package/example_slides/075-mermaid-columns.md +32 -0
- package/example_slides/076-section-addons.md +14 -0
- package/example_slides/077-asciinema.md +27 -0
- package/example_slides/078-fancyarrow.md +31 -0
- package/example_slides/079-fancyarrow-demo.md +23 -0
- package/example_slides/080-section-theme.md +14 -0
- package/example_slides/081-color-architecture.md +22 -0
- package/example_slides/082-semantic-text-colors.md +25 -0
- package/example_slides/083-typography.md +16 -0
- package/example_slides/084-typography-rationale.md +22 -0
- package/example_slides/085-icons.md +24 -0
- package/example_slides/086-tables.md +14 -0
- package/example_slides/087-code-blocks.md +18 -0
- package/example_slides/088-motion-modes.md +35 -0
- package/example_slides/089-slide-transitions.md +31 -0
- package/example_slides/090-v-click-reveals.md +40 -0
- package/example_slides/091-accessibility.md +27 -0
- package/example_slides/092-safe-zone.md +17 -0
- package/example_slides/093-questions.md +8 -0
- package/example_white.md +10 -0
- package/fonts/IBMPlexMono-Medium.woff2 +1449 -0
- package/fonts/IBMPlexMono-Regular.woff2 +1449 -0
- package/fonts/IBMPlexSans-Bold.woff2 +1449 -0
- package/fonts/IBMPlexSans-Medium.woff2 +1449 -0
- package/fonts/IBMPlexSans-Regular.woff2 +1449 -0
- package/fonts/IBMPlexSans-SemiBold.woff2 +1449 -0
- package/fonts/LICENSE.txt +93 -0
- package/layouts/slide.vue +251 -0
- package/package.json +62 -0
- package/public/avatars/alice.png +0 -0
- package/public/avatars/bob.png +0 -0
- package/public/avatars/carol.png +0 -0
- package/scripts/chart-audit.mjs +216 -0
- package/scripts/contrast-audit.mjs +299 -0
- package/scripts/generate-palette.mjs +395 -0
- package/scripts/integrity-audit.mjs +357 -0
- package/scripts/shared/css-utils.mjs +216 -0
- package/scripts/shiki-audit.mjs +300 -0
- package/scripts/typography-audit.mjs +300 -0
- package/setup/main.ts +107 -0
- package/setup/mermaid.ts +237 -0
- package/setup/shiki.ts +40 -0
- package/snippets/demo.ts +26 -0
- package/styles/base.css +1053 -0
- package/styles/colors.css +422 -0
- package/styles/index.css +12 -0
- package/styles/motion.css +486 -0
- package/uno.config.ts +67 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* StyledList primitive — themed list markers, columns, and formatting.
|
|
4
|
+
*
|
|
5
|
+
* Wraps markdown list content and applies styled markers.
|
|
6
|
+
*
|
|
7
|
+
* <StyledList marker="check" color="success">
|
|
8
|
+
*
|
|
9
|
+
* - Item one
|
|
10
|
+
* - Item two
|
|
11
|
+
*
|
|
12
|
+
* </StyledList>
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { SemanticColor } from '../composables/useColors'
|
|
16
|
+
import { semanticColorVar } from '../composables/useColors'
|
|
17
|
+
|
|
18
|
+
const props = withDefaults(defineProps<{
|
|
19
|
+
marker?: 'disc' | 'number' | 'check' | 'arrow' | 'star' | 'none'
|
|
20
|
+
color?: SemanticColor
|
|
21
|
+
size?: 'sm' | 'md' | 'lg'
|
|
22
|
+
columns?: 1 | 2
|
|
23
|
+
indent?: 'normal' | 'hanging'
|
|
24
|
+
align?: 'left' | 'center'
|
|
25
|
+
}>(), {
|
|
26
|
+
marker: 'disc',
|
|
27
|
+
color: 'primary',
|
|
28
|
+
size: 'md',
|
|
29
|
+
columns: 1,
|
|
30
|
+
indent: 'normal',
|
|
31
|
+
align: 'left',
|
|
32
|
+
})
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
<template>
|
|
36
|
+
<div
|
|
37
|
+
class="styled-list"
|
|
38
|
+
:class="[
|
|
39
|
+
`styled-list-${marker}`,
|
|
40
|
+
`styled-list-${size}`,
|
|
41
|
+
{ 'styled-list-cols-2': columns === 2 },
|
|
42
|
+
{ 'styled-list-hanging': indent === 'hanging' },
|
|
43
|
+
{ 'styled-list-center': align === 'center' },
|
|
44
|
+
]"
|
|
45
|
+
:style="{ '--marker-color': semanticColorVar[color] }"
|
|
46
|
+
>
|
|
47
|
+
<slot />
|
|
48
|
+
</div>
|
|
49
|
+
</template>
|
|
50
|
+
|
|
51
|
+
<style>
|
|
52
|
+
.styled-list ul,
|
|
53
|
+
.styled-list ol {
|
|
54
|
+
list-style: none;
|
|
55
|
+
margin-inline-start: 0;
|
|
56
|
+
max-width: none;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.styled-list li {
|
|
60
|
+
list-style: none;
|
|
61
|
+
padding-left: 2em;
|
|
62
|
+
position: relative;
|
|
63
|
+
margin-block: 0.5em;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* Size variants */
|
|
67
|
+
.styled-list-sm li { font-size: var(--font-size-small); }
|
|
68
|
+
.styled-list-md li { font-size: var(--font-size-base); }
|
|
69
|
+
.styled-list-lg li { font-size: calc(var(--font-size-base) * 1.15); }
|
|
70
|
+
|
|
71
|
+
/* Marker: disc (default) */
|
|
72
|
+
.styled-list-disc ul { margin-inline-start: 1.5em; }
|
|
73
|
+
.styled-list-disc li { list-style: disc; padding-left: 0; }
|
|
74
|
+
|
|
75
|
+
/* Marker: check */
|
|
76
|
+
.styled-list-check li::before {
|
|
77
|
+
content: '\2713';
|
|
78
|
+
position: absolute;
|
|
79
|
+
left: 0;
|
|
80
|
+
color: var(--marker-color);
|
|
81
|
+
font-weight: var(--font-weight-bold);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* Marker: arrow */
|
|
85
|
+
.styled-list-arrow li::before {
|
|
86
|
+
content: '\2192';
|
|
87
|
+
position: absolute;
|
|
88
|
+
left: 0;
|
|
89
|
+
color: var(--marker-color);
|
|
90
|
+
font-weight: var(--font-weight-bold);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* Marker: star */
|
|
94
|
+
.styled-list-star li::before {
|
|
95
|
+
content: '\2605';
|
|
96
|
+
position: absolute;
|
|
97
|
+
left: 0;
|
|
98
|
+
color: var(--marker-color);
|
|
99
|
+
font-weight: var(--font-weight-bold);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/* Marker: number */
|
|
103
|
+
.styled-list-number ul,
|
|
104
|
+
.styled-list-number ol {
|
|
105
|
+
counter-reset: styled-list;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.styled-list-number li {
|
|
109
|
+
counter-increment: styled-list;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.styled-list-number li::before {
|
|
113
|
+
content: counter(styled-list) '.';
|
|
114
|
+
position: absolute;
|
|
115
|
+
left: 0;
|
|
116
|
+
color: var(--marker-color);
|
|
117
|
+
font-weight: var(--font-weight-bold);
|
|
118
|
+
font-size: var(--font-size-base);
|
|
119
|
+
width: 1.5em;
|
|
120
|
+
text-align: center;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* Marker: none */
|
|
124
|
+
.styled-list-none li { padding-left: 0; }
|
|
125
|
+
.styled-list-none li::before { display: none; }
|
|
126
|
+
|
|
127
|
+
/* Columns */
|
|
128
|
+
.styled-list-cols-2 ul,
|
|
129
|
+
.styled-list-cols-2 ol {
|
|
130
|
+
columns: 2;
|
|
131
|
+
column-gap: var(--space-xl);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.styled-list-cols-2 li {
|
|
135
|
+
break-inside: avoid;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/* Hanging indent (for references) */
|
|
139
|
+
.styled-list-hanging li {
|
|
140
|
+
padding-left: 2em;
|
|
141
|
+
text-indent: -2em;
|
|
142
|
+
line-height: var(--line-height-reading);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.styled-list-hanging li::before { display: none; }
|
|
146
|
+
|
|
147
|
+
/* Center alignment */
|
|
148
|
+
.styled-list-center ul,
|
|
149
|
+
.styled-list-center ol {
|
|
150
|
+
text-align: center;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.styled-list-center li {
|
|
154
|
+
margin-block: 0.75em;
|
|
155
|
+
}
|
|
156
|
+
</style>
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* StyledText — universal themed text component.
|
|
4
|
+
*
|
|
5
|
+
* Replaces markdown headings/text when you need color, effects, or formatting.
|
|
6
|
+
* Use plain markdown (#, ##, ###, paragraphs) when no styling is needed.
|
|
7
|
+
*
|
|
8
|
+
* Heading (renders <h1>/<h2>/<h3>):
|
|
9
|
+
* <StyledText tag="h1" color="primary">Colored Title</StyledText>
|
|
10
|
+
* <StyledText tag="h2" align="center">Centered H2</StyledText>
|
|
11
|
+
*
|
|
12
|
+
* Block (default, renders <p>):
|
|
13
|
+
* <StyledText color="secondary">Subtitle text</StyledText>
|
|
14
|
+
* <StyledText size="small" color="tertiary">Caption</StyledText>
|
|
15
|
+
*
|
|
16
|
+
* Inline (renders <span>):
|
|
17
|
+
* <StyledText inline color="primary">colored word</StyledText>
|
|
18
|
+
* <StyledText inline highlight="warning">highlighted</StyledText>
|
|
19
|
+
* <StyledText inline kbd>Ctrl+S</StyledText>
|
|
20
|
+
* <StyledText inline gradient>fancy text</StyledText>
|
|
21
|
+
* <StyledText inline bold color="danger">bold danger</StyledText>
|
|
22
|
+
*
|
|
23
|
+
* Spoiler (auto-integrates with Slidev v-click):
|
|
24
|
+
* <StyledText inline spoiler>hidden answer</StyledText>
|
|
25
|
+
* Automatically registers as next v-click. Reveals on slide advance,
|
|
26
|
+
* re-blurs on backward navigation. Falls back to manual click outside Slidev.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { ref, computed, inject, onMounted, onUnmounted, watchEffect } from 'vue'
|
|
30
|
+
import type { Ref } from 'vue'
|
|
31
|
+
import type { TextColor } from '../composables/useColors'
|
|
32
|
+
import { textColorVar, semanticColorVar, gradientVar } from '../composables/useColors'
|
|
33
|
+
|
|
34
|
+
type HighlightColor = 'primary' | 'success' | 'warning' | 'danger' | 'info'
|
|
35
|
+
type TagName = 'h1' | 'h2' | 'h3' | 'p' | 'span'
|
|
36
|
+
|
|
37
|
+
const props = defineProps<{
|
|
38
|
+
// HTML tag (h1/h2/h3/p/span). Default: p. `inline` is shorthand for span.
|
|
39
|
+
tag?: TagName
|
|
40
|
+
inline?: boolean
|
|
41
|
+
// Block-only props
|
|
42
|
+
align?: 'left' | 'center' | 'right'
|
|
43
|
+
// Shared props
|
|
44
|
+
color?: TextColor
|
|
45
|
+
size?: 'small' | 'base' | 'large'
|
|
46
|
+
// Inline formatting props
|
|
47
|
+
bold?: boolean
|
|
48
|
+
italic?: boolean
|
|
49
|
+
underline?: boolean
|
|
50
|
+
strike?: boolean
|
|
51
|
+
uppercase?: boolean
|
|
52
|
+
mono?: boolean
|
|
53
|
+
gradient?: boolean
|
|
54
|
+
kbd?: boolean
|
|
55
|
+
highlight?: HighlightColor
|
|
56
|
+
spoiler?: boolean
|
|
57
|
+
}>()
|
|
58
|
+
|
|
59
|
+
const resolvedTag = computed<TagName>(() => {
|
|
60
|
+
if (props.tag) return props.tag
|
|
61
|
+
return props.inline ? 'span' : 'p'
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
const isInline = computed(() => resolvedTag.value === 'span')
|
|
65
|
+
const isHeading = computed(() => ['h1', 'h2', 'h3'].includes(resolvedTag.value))
|
|
66
|
+
|
|
67
|
+
// --- Spoiler: auto v-click integration ---
|
|
68
|
+
const rootEl = ref<HTMLElement | null>(null)
|
|
69
|
+
const clicksContext = inject<Ref<any>>('$$slidev-clicks-context', ref(null))
|
|
70
|
+
const revealed = ref(false)
|
|
71
|
+
const hasSlidevClicks = computed(() => !!clicksContext.value?.calculate)
|
|
72
|
+
|
|
73
|
+
if (props.spoiler) {
|
|
74
|
+
onMounted(() => {
|
|
75
|
+
const ctx = clicksContext.value
|
|
76
|
+
if (ctx?.calculate && rootEl.value) {
|
|
77
|
+
const info = ctx.calculate('+1')
|
|
78
|
+
if (info) {
|
|
79
|
+
ctx.register(rootEl.value, info)
|
|
80
|
+
// Track click state reactively — info.isActive is a computed ref
|
|
81
|
+
watchEffect(() => {
|
|
82
|
+
revealed.value = info.isActive.value
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
onUnmounted(() => {
|
|
89
|
+
if (clicksContext.value?.unregister && rootEl.value) {
|
|
90
|
+
clicksContext.value.unregister(rootEl.value)
|
|
91
|
+
}
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function handleSpoilerClick(e: Event) {
|
|
96
|
+
if (!props.spoiler) return
|
|
97
|
+
if (hasSlidevClicks.value) return
|
|
98
|
+
e.stopPropagation()
|
|
99
|
+
revealed.value = !revealed.value
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function handleSpoilerKey(e: KeyboardEvent) {
|
|
103
|
+
if (!props.spoiler || hasSlidevClicks.value) return
|
|
104
|
+
if (e.key === ' ') e.preventDefault()
|
|
105
|
+
revealed.value = !revealed.value
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// --- CSS variables ---
|
|
109
|
+
const cssVars = computed(() => {
|
|
110
|
+
const vars: Record<string, string> = {}
|
|
111
|
+
if (props.color && props.color !== 'inherit' && !props.gradient)
|
|
112
|
+
vars['--text-color'] = textColorVar[props.color]
|
|
113
|
+
if (props.gradient && props.color && props.color !== 'inherit')
|
|
114
|
+
vars['--text-gradient'] = gradientVar[props.color as keyof typeof gradientVar] || gradientVar.primary
|
|
115
|
+
if (props.highlight)
|
|
116
|
+
vars['--text-highlight-bg'] = semanticColorVar[props.highlight]
|
|
117
|
+
return vars
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
const sizeMap: Record<string, string> = {
|
|
121
|
+
base: 'var(--font-size-base)',
|
|
122
|
+
small: 'var(--font-size-small)',
|
|
123
|
+
large: 'var(--font-size-h2)',
|
|
124
|
+
}
|
|
125
|
+
</script>
|
|
126
|
+
|
|
127
|
+
<template>
|
|
128
|
+
<component
|
|
129
|
+
ref="rootEl"
|
|
130
|
+
:is="resolvedTag"
|
|
131
|
+
:class="[
|
|
132
|
+
isInline ? 'styled-text-inline' : 'styled-text-block',
|
|
133
|
+
{
|
|
134
|
+
'is-bold': bold,
|
|
135
|
+
'is-italic': italic,
|
|
136
|
+
'is-underline': underline,
|
|
137
|
+
'is-strike': strike,
|
|
138
|
+
'is-spoiler': spoiler && !revealed,
|
|
139
|
+
'is-spoiler-revealed': spoiler && revealed,
|
|
140
|
+
'is-mono': mono,
|
|
141
|
+
'is-uppercase': uppercase,
|
|
142
|
+
'is-gradient': gradient,
|
|
143
|
+
'is-highlight': highlight,
|
|
144
|
+
'is-kbd': kbd,
|
|
145
|
+
'is-size-small': size === 'small',
|
|
146
|
+
'is-size-large': size === 'large',
|
|
147
|
+
},
|
|
148
|
+
]"
|
|
149
|
+
:style="{
|
|
150
|
+
...cssVars,
|
|
151
|
+
...(isInline ? {} : {
|
|
152
|
+
textAlign: align || undefined,
|
|
153
|
+
...(isHeading ? {} : { fontSize: sizeMap[size || 'base'] }),
|
|
154
|
+
}),
|
|
155
|
+
}"
|
|
156
|
+
:role="spoiler && !hasSlidevClicks ? 'button' : undefined"
|
|
157
|
+
:tabindex="spoiler && !hasSlidevClicks ? 0 : undefined"
|
|
158
|
+
:aria-label="spoiler && !revealed ? 'Spoiler — click to reveal' : undefined"
|
|
159
|
+
@click="handleSpoilerClick"
|
|
160
|
+
@keydown.enter="handleSpoilerKey"
|
|
161
|
+
@keydown.space="handleSpoilerKey"
|
|
162
|
+
>
|
|
163
|
+
<slot />
|
|
164
|
+
</component>
|
|
165
|
+
</template>
|
|
166
|
+
|
|
167
|
+
<style scoped>
|
|
168
|
+
/* === Block mode (default) === */
|
|
169
|
+
.styled-text-block {
|
|
170
|
+
color: var(--text-color, inherit);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* === Inline mode === */
|
|
174
|
+
.styled-text-inline {
|
|
175
|
+
color: var(--text-color, inherit);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/* --- Formatting --- */
|
|
179
|
+
.is-bold { font-weight: 700; }
|
|
180
|
+
.is-italic { font-style: italic; }
|
|
181
|
+
.is-underline { text-decoration: underline; text-underline-offset: 0.15em; }
|
|
182
|
+
.is-strike { text-decoration: line-through; }
|
|
183
|
+
|
|
184
|
+
.is-underline.is-strike {
|
|
185
|
+
text-decoration: underline line-through;
|
|
186
|
+
text-underline-offset: 0.15em;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/* --- Sizes (inline mode) --- */
|
|
190
|
+
.styled-text-inline.is-size-small { font-size: var(--font-size-small); }
|
|
191
|
+
.styled-text-inline.is-size-large { font-size: var(--font-size-h2); }
|
|
192
|
+
|
|
193
|
+
/* --- Spoiler --- */
|
|
194
|
+
.is-spoiler {
|
|
195
|
+
filter: blur(0.45em);
|
|
196
|
+
cursor: pointer;
|
|
197
|
+
transition: filter var(--duration-moderate, 240ms) var(--ease-standard, ease);
|
|
198
|
+
user-select: none;
|
|
199
|
+
border-radius: 0.15em;
|
|
200
|
+
background: var(--color-bg-muted);
|
|
201
|
+
padding: 0.1em 0.25em;
|
|
202
|
+
margin: 0 0.1em;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.is-spoiler:hover {
|
|
206
|
+
filter: blur(0.3em);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.is-spoiler:focus-visible {
|
|
210
|
+
outline: 3px solid var(--color-primary);
|
|
211
|
+
outline-offset: 2px;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.is-spoiler-revealed {
|
|
215
|
+
cursor: default;
|
|
216
|
+
user-select: auto;
|
|
217
|
+
border-radius: 0.15em;
|
|
218
|
+
background: var(--color-bg-muted);
|
|
219
|
+
padding: 0.1em 0.25em;
|
|
220
|
+
margin: 0 0.1em;
|
|
221
|
+
animation: spoiler-reveal var(--duration-moderate, 240ms) var(--ease-standard, ease);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
@keyframes spoiler-reveal {
|
|
225
|
+
from { filter: blur(0.45em); }
|
|
226
|
+
to { filter: blur(0); }
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/* --- Monospace --- */
|
|
230
|
+
.is-mono {
|
|
231
|
+
font-family: 'IBM Plex Mono', monospace;
|
|
232
|
+
font-size: 0.9em;
|
|
233
|
+
background: var(--color-bg-muted);
|
|
234
|
+
padding: 0.1em 0.35em;
|
|
235
|
+
border-radius: 0.2em;
|
|
236
|
+
margin: 0 0.1em;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/* --- Uppercase --- */
|
|
240
|
+
.is-uppercase {
|
|
241
|
+
text-transform: uppercase;
|
|
242
|
+
letter-spacing: 0.05em;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/* --- Gradient text --- */
|
|
246
|
+
.is-gradient {
|
|
247
|
+
background: var(--text-gradient, var(--gradient-primary));
|
|
248
|
+
-webkit-background-clip: text;
|
|
249
|
+
-webkit-text-fill-color: transparent;
|
|
250
|
+
background-clip: text;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/* --- Highlight --- */
|
|
254
|
+
.is-highlight {
|
|
255
|
+
background-color: color-mix(in srgb, var(--text-highlight-bg) 20%, transparent);
|
|
256
|
+
padding: 0.1em 0.25em;
|
|
257
|
+
border-radius: 0.2em;
|
|
258
|
+
border-bottom: 2px solid var(--text-highlight-bg);
|
|
259
|
+
margin: 0 0.1em;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/* --- Keyboard key --- */
|
|
263
|
+
.is-kbd {
|
|
264
|
+
font-family: 'IBM Plex Mono', monospace;
|
|
265
|
+
font-size: 0.85em;
|
|
266
|
+
background: var(--color-bg-soft);
|
|
267
|
+
border: 1px solid var(--color-border);
|
|
268
|
+
border-bottom-width: 2px;
|
|
269
|
+
padding: 0.1em 0.4em;
|
|
270
|
+
border-radius: 0.25em;
|
|
271
|
+
box-shadow: 0 1px 0 var(--color-border);
|
|
272
|
+
white-space: nowrap;
|
|
273
|
+
margin: 0 0.1em;
|
|
274
|
+
}
|
|
275
|
+
</style>
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* SwotGrid widget — SWOT analysis built on top of MatrixGrid.
|
|
4
|
+
*
|
|
5
|
+
* <SwotGrid
|
|
6
|
+
* :strengths="['Strong brand', 'Expert team']"
|
|
7
|
+
* :weaknesses="['Limited reach', 'High cost']"
|
|
8
|
+
* :opportunities="['New markets', 'Partnerships']"
|
|
9
|
+
* :threats="['Competition', 'Regulation']"
|
|
10
|
+
* />
|
|
11
|
+
*/
|
|
12
|
+
import { computed } from 'vue'
|
|
13
|
+
|
|
14
|
+
const props = withDefaults(defineProps<{
|
|
15
|
+
strengths: string[]
|
|
16
|
+
weaknesses: string[]
|
|
17
|
+
opportunities: string[]
|
|
18
|
+
threats: string[]
|
|
19
|
+
showAxes?: boolean
|
|
20
|
+
}>(), {
|
|
21
|
+
showAxes: true,
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
const cells = computed(() => [
|
|
25
|
+
{ title: 'Strengths', items: props.strengths, color: 'success' as const },
|
|
26
|
+
{ title: 'Weaknesses', items: props.weaknesses, color: 'danger' as const },
|
|
27
|
+
{ title: 'Opportunities', items: props.opportunities, color: 'primary' as const },
|
|
28
|
+
{ title: 'Threats', items: props.threats, color: 'warning' as const },
|
|
29
|
+
])
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<template>
|
|
33
|
+
<div class="swot-wrapper" :class="{ 'swot-with-axes': showAxes }">
|
|
34
|
+
<div v-if="showAxes" class="swot-axis-top">
|
|
35
|
+
<span class="swot-axis-label">Helpful</span>
|
|
36
|
+
<span class="swot-axis-label">Harmful</span>
|
|
37
|
+
</div>
|
|
38
|
+
<div class="swot-body">
|
|
39
|
+
<div v-if="showAxes" class="swot-axis-left">
|
|
40
|
+
<span class="swot-axis-label-vertical">Internal</span>
|
|
41
|
+
<span class="swot-axis-label-vertical">External</span>
|
|
42
|
+
</div>
|
|
43
|
+
<MatrixGrid :cells="cells" />
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
</template>
|
|
47
|
+
|
|
48
|
+
<style scoped>
|
|
49
|
+
.swot-wrapper {
|
|
50
|
+
width: 100%;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.swot-axis-top {
|
|
54
|
+
display: grid;
|
|
55
|
+
grid-template-columns: 1fr 1fr;
|
|
56
|
+
text-align: center;
|
|
57
|
+
padding-bottom: var(--space-xs);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.swot-with-axes .swot-axis-top {
|
|
61
|
+
margin-left: 2rem;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.swot-axis-label {
|
|
65
|
+
font-size: var(--font-size-small);
|
|
66
|
+
font-weight: var(--font-weight-semibold);
|
|
67
|
+
color: var(--color-text-secondary);
|
|
68
|
+
text-transform: uppercase;
|
|
69
|
+
letter-spacing: 0.05em;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.swot-body {
|
|
73
|
+
display: flex;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.swot-axis-left {
|
|
77
|
+
display: flex;
|
|
78
|
+
flex-direction: column;
|
|
79
|
+
justify-content: space-around;
|
|
80
|
+
width: 2rem;
|
|
81
|
+
flex-shrink: 0;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.swot-axis-label-vertical {
|
|
85
|
+
font-size: var(--font-size-small);
|
|
86
|
+
font-weight: var(--font-weight-semibold);
|
|
87
|
+
color: var(--color-text-secondary);
|
|
88
|
+
text-transform: uppercase;
|
|
89
|
+
letter-spacing: 0.05em;
|
|
90
|
+
writing-mode: vertical-lr;
|
|
91
|
+
transform: rotate(180deg);
|
|
92
|
+
text-align: center;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.swot-body :deep(.matrix-grid) {
|
|
96
|
+
flex: 1;
|
|
97
|
+
margin-top: 0;
|
|
98
|
+
}
|
|
99
|
+
</style>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Tags component - теги/мітки
|
|
4
|
+
*
|
|
5
|
+
* Використання:
|
|
6
|
+
* <Tags :items="['Vue', 'TypeScript', 'Slidev']" />
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
defineProps<{
|
|
10
|
+
items: string[]
|
|
11
|
+
}>()
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<template>
|
|
15
|
+
<div class="tags-container">
|
|
16
|
+
<span v-for="item in items" :key="item" class="tag">
|
|
17
|
+
{{ item }}
|
|
18
|
+
</span>
|
|
19
|
+
</div>
|
|
20
|
+
</template>
|