hexo-design-cards 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/.github/workflows/ci.yml +21 -0
- package/CHANGELOG.md +12 -0
- package/LICENSE +21 -0
- package/README.md +218 -0
- package/index.js +21 -0
- package/lib/colorways.js +41 -0
- package/lib/css.js +65 -0
- package/package.json +34 -0
- package/screenshots/00-banner.png +0 -0
- package/screenshots/01-cards.png +0 -0
- package/screenshots/02-accent-cards.png +0 -0
- package/screenshots/03-compare.png +0 -0
- package/screenshots/04-alert.png +0 -0
- package/screenshots/05-quotes.png +0 -0
- package/screenshots/06-mini-cards.png +0 -0
- package/screenshots/07-flow-1.png +0 -0
- package/screenshots/07-flow-2.png +0 -0
- package/tags/accents.js +32 -0
- package/tags/alert.js +35 -0
- package/tags/banner.js +11 -0
- package/tags/cards.js +33 -0
- package/tags/compare.js +36 -0
- package/tags/flow.js +46 -0
- package/tags/minicards.js +27 -0
- package/tags/quotes.js +30 -0
- package/test/mock-hexo.js +43 -0
- package/test/tags.test.js +228 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
node-version: [18, 20, 22]
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: actions/setup-node@v4
|
|
18
|
+
with:
|
|
19
|
+
node-version: ${{ matrix.node-version }}
|
|
20
|
+
- run: npm install
|
|
21
|
+
- run: npm test
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.1.0] - 2026-03-01
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- 8 tag plugins: `banner`, `cards`, `accents`, `compare`, `alert`, `quotes`, `minicards`, `flow`
|
|
7
|
+
- 5 colorways: deep-sea, olive-garden, fiery-ocean, rustic-earth, sunny-beach
|
|
8
|
+
- Per-post colorway override via front matter
|
|
9
|
+
- Optional font size parameter for cards, accents, compare, alert
|
|
10
|
+
- CSS auto-injection via `after_render:html` filter
|
|
11
|
+
- Responsive layout (single-column on mobile)
|
|
12
|
+
- Markdown rendering inside card/accent/compare/alert/mini content
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 leafbird
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# hexo-design-cards
|
|
2
|
+
|
|
3
|
+
[](https://github.com/leafbird/hexo-design-cards/actions/workflows/ci.yml) [](https://github.com/leafbird/hexo-design-cards/releases) [](https://opensource.org/licenses/MIT)
|
|
4
|
+
|
|
5
|
+
Beautiful design card tags for [Hexo](https://hexo.io) — replace verbose inline HTML with clean tag syntax.
|
|
6
|
+
|
|
7
|
+
> Flow diagrams, header cards, accent cards, comparison cards, quotes, alerts, mini cards, and section banners with customizable colorways.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
### From GitHub Packages
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# Add to .npmrc in your Hexo project
|
|
15
|
+
echo "@leafbird:registry=https://npm.pkg.github.com" >> .npmrc
|
|
16
|
+
echo "//npm.pkg.github.com/:_authToken=YOUR_GITHUB_TOKEN" >> .npmrc
|
|
17
|
+
|
|
18
|
+
npm install @leafbird/hexo-design-cards
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### From npm (coming soon)
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install hexo-design-cards
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
After installing, use tag syntax in your Hexo markdown files. No additional configuration needed — the plugin auto-injects CSS and uses the **Deep Sea** colorway by default.
|
|
30
|
+
|
|
31
|
+
## Tags
|
|
32
|
+
|
|
33
|
+
### Banner
|
|
34
|
+
|
|
35
|
+
Section divider with a bold colored bar.
|
|
36
|
+
|
|
37
|
+
```markdown
|
|
38
|
+
{% banner "Section 1: Getting Started" %}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+

|
|
42
|
+
|
|
43
|
+
### Cards
|
|
44
|
+
|
|
45
|
+
Grid of cards with colored headers. Great for comparing concepts side by side.
|
|
46
|
+
|
|
47
|
+
```markdown
|
|
48
|
+
{% cards 2 %}
|
|
49
|
+
{% card "Title A" %}
|
|
50
|
+
Description with **markdown** support.
|
|
51
|
+
`code snippets` work too.
|
|
52
|
+
{% endcard %}
|
|
53
|
+
{% card "Title B" %}
|
|
54
|
+
Another card's content.
|
|
55
|
+
{% endcard %}
|
|
56
|
+
{% endcards %}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
- First argument: number of columns (1–4)
|
|
60
|
+
- Optional last argument: font size in px (e.g. `{% cards 2 15 %}`)
|
|
61
|
+
|
|
62
|
+

|
|
63
|
+
|
|
64
|
+
### Accent Cards
|
|
65
|
+
|
|
66
|
+
Cards with a colored left border. Perfect for highlighting key points.
|
|
67
|
+
|
|
68
|
+
```markdown
|
|
69
|
+
{% accents 2 %}
|
|
70
|
+
{% accent "Point 1" %}Description of the first point{% endaccent %}
|
|
71
|
+
{% accent "Point 2" %}Description of the second point{% endaccent %}
|
|
72
|
+
{% accent "Point 3" %}Third point here{% endaccent %}
|
|
73
|
+
{% accent "Point 4" %}Fourth point here{% endaccent %}
|
|
74
|
+
{% endaccents %}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
- First argument: number of columns (1–4)
|
|
78
|
+
- Optional last argument: font size in px
|
|
79
|
+
|
|
80
|
+

|
|
81
|
+
|
|
82
|
+
### Compare
|
|
83
|
+
|
|
84
|
+
Side-by-side comparison of two options.
|
|
85
|
+
|
|
86
|
+
```markdown
|
|
87
|
+
{% compare %}
|
|
88
|
+
{% option "Option A" "🔧" %}
|
|
89
|
+
Description of option A.
|
|
90
|
+
{% endoption %}
|
|
91
|
+
{% option "Option B" "🚀" recommended %}
|
|
92
|
+
Description of option B.
|
|
93
|
+
This one is **recommended**.
|
|
94
|
+
{% endoption %}
|
|
95
|
+
{% endcompare %}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
- Second argument (optional): emoji
|
|
99
|
+
- `recommended` flag: thicker border for emphasis
|
|
100
|
+
- Optional font size: `{% compare 15 %}`
|
|
101
|
+
|
|
102
|
+

|
|
103
|
+
|
|
104
|
+
### Alert
|
|
105
|
+
|
|
106
|
+
Info, warning, or tip box.
|
|
107
|
+
|
|
108
|
+
```markdown
|
|
109
|
+
{% alert info %}Title|Body text with **markdown**{% endalert %}
|
|
110
|
+
{% alert warning %}Warning title|Warning body{% endalert %}
|
|
111
|
+
{% alert tip %}Tip title|Tip body{% endalert %}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
- Types: `info` (ℹ️), `warning` (⚠️), `tip` (💡)
|
|
115
|
+
- Use `|` to separate title and body (optional — omit for no title)
|
|
116
|
+
- Optional font size: `{% alert warning 17 %}`
|
|
117
|
+
|
|
118
|
+

|
|
119
|
+
|
|
120
|
+
### Quotes
|
|
121
|
+
|
|
122
|
+
A collection of styled quotes with colored left borders.
|
|
123
|
+
|
|
124
|
+
```markdown
|
|
125
|
+
{% quotes "Section Title" %}
|
|
126
|
+
{% dcquote "Source 1" %}Quote text here{% enddcquote %}
|
|
127
|
+
{% dcquote "Source 2" %}Another quote{% enddcquote %}
|
|
128
|
+
{% endquotes %}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+

|
|
132
|
+
|
|
133
|
+
### Mini Cards
|
|
134
|
+
|
|
135
|
+
Compact cards in a 3-column grid. Good for listing short items.
|
|
136
|
+
|
|
137
|
+
```markdown
|
|
138
|
+
{% minicards %}
|
|
139
|
+
{% mini "Item A" %}Short description{% endmini %}
|
|
140
|
+
{% mini "Item B" %}Short description{% endmini %}
|
|
141
|
+
{% mini "Item C" %}Short description{% endmini %}
|
|
142
|
+
{% endminicards %}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+

|
|
146
|
+
|
|
147
|
+
### Flow
|
|
148
|
+
|
|
149
|
+
Simple horizontal flow diagram with arrow connectors.
|
|
150
|
+
|
|
151
|
+
```markdown
|
|
152
|
+
{% flow "Step A|description" "*Step B|description" "Step C|description" %}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
- `*` prefix: highlighted step (bold border)
|
|
156
|
+
- `|` inside quotes: separates title and description
|
|
157
|
+
- Trailing `|` after all steps: caption text
|
|
158
|
+
|
|
159
|
+
```markdown
|
|
160
|
+
{% flow "Request" "*Process" "Response" | Data flow overview %}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+

|
|
164
|
+

|
|
165
|
+
|
|
166
|
+
## Font Size Parameter
|
|
167
|
+
|
|
168
|
+
Most container tags accept an optional font size (in px) as the last numeric argument:
|
|
169
|
+
|
|
170
|
+
```markdown
|
|
171
|
+
{% cards 2 15 %}...{% endcards %} → 2 columns, 15px body text
|
|
172
|
+
{% accents 2 14 %}...{% endaccents %} → 2 columns, 14px body text
|
|
173
|
+
{% compare 16 %}...{% endcompare %} → 16px body text
|
|
174
|
+
{% alert warning 17 %}...{% endalert %} → 17px body text
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
When omitted, the plugin's CSS defaults apply.
|
|
178
|
+
|
|
179
|
+
## Colorways
|
|
180
|
+
|
|
181
|
+
Five built-in color palettes. Each has 5 colors (C1–C5) from darkest to lightest.
|
|
182
|
+
|
|
183
|
+
| Colorway | Vibe |
|
|
184
|
+
|----------|------|
|
|
185
|
+
| `deep-sea` (default) | Calm blue-grey |
|
|
186
|
+
| `olive-garden` | Warm olive-gold |
|
|
187
|
+
| `fiery-ocean` | Bold red-blue contrast |
|
|
188
|
+
| `rustic-earth` | Natural earth tones |
|
|
189
|
+
| `sunny-beach` | Vivid orange-teal |
|
|
190
|
+
|
|
191
|
+
Color palettes from [Coolors.co](https://coolors.co).
|
|
192
|
+
|
|
193
|
+
### Global Setting
|
|
194
|
+
|
|
195
|
+
In your Hexo `_config.yml`:
|
|
196
|
+
|
|
197
|
+
```yaml
|
|
198
|
+
design_cards:
|
|
199
|
+
colorway: deep-sea
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Per-Post Override
|
|
203
|
+
|
|
204
|
+
In a post's front matter:
|
|
205
|
+
|
|
206
|
+
```yaml
|
|
207
|
+
---
|
|
208
|
+
colorway: fiery-ocean
|
|
209
|
+
---
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Responsive
|
|
213
|
+
|
|
214
|
+
All grid layouts collapse to single-column on screens narrower than 768px. Flow diagrams switch to vertical layout.
|
|
215
|
+
|
|
216
|
+
## License
|
|
217
|
+
|
|
218
|
+
[MIT](LICENSE)
|
package/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { getCSS } = require('./lib/css');
|
|
4
|
+
|
|
5
|
+
// Register all tag plugins
|
|
6
|
+
require('./tags/flow')(hexo);
|
|
7
|
+
require('./tags/cards')(hexo);
|
|
8
|
+
require('./tags/accents')(hexo);
|
|
9
|
+
require('./tags/compare')(hexo);
|
|
10
|
+
require('./tags/quotes')(hexo);
|
|
11
|
+
require('./tags/alert')(hexo);
|
|
12
|
+
require('./tags/minicards')(hexo);
|
|
13
|
+
require('./tags/banner')(hexo);
|
|
14
|
+
|
|
15
|
+
// Inject CSS once per page
|
|
16
|
+
hexo.extend.filter.register('after_render:html', function(str) {
|
|
17
|
+
if (str.indexOf('data-hexo-design-cards') > -1) return str;
|
|
18
|
+
if (str.indexOf('dc-') === -1) return str; // no design cards used
|
|
19
|
+
const css = getCSS();
|
|
20
|
+
return str.replace('</head>', css + '\n</head>');
|
|
21
|
+
});
|
package/lib/colorways.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const colorways = {
|
|
4
|
+
'deep-sea': {
|
|
5
|
+
name: 'Deep Sea',
|
|
6
|
+
c1: '#0d1b2a', c2: '#1b263b', c3: '#415a77', c4: '#778da9', c5: '#e0e1dd'
|
|
7
|
+
},
|
|
8
|
+
'olive-garden': {
|
|
9
|
+
name: 'Olive Garden Feast',
|
|
10
|
+
c1: '#283618', c2: '#606c38', c3: '#bc6c25', c4: '#dda15e', c5: '#fefae0'
|
|
11
|
+
},
|
|
12
|
+
'fiery-ocean': {
|
|
13
|
+
name: 'Fiery Ocean',
|
|
14
|
+
c1: '#780000', c2: '#c1121f', c3: '#003049', c4: '#669bbc', c5: '#fdf0d5'
|
|
15
|
+
},
|
|
16
|
+
'rustic-earth': {
|
|
17
|
+
name: 'Rustic Earthy Tones',
|
|
18
|
+
c1: '#414833', c2: '#656d4a', c3: '#7f5539', c4: '#a68a64', c5: '#ede0d4'
|
|
19
|
+
},
|
|
20
|
+
'sunny-beach': {
|
|
21
|
+
name: 'Sunny Beach Day',
|
|
22
|
+
c1: '#001524', c2: '#78290f', c3: '#15616d', c4: '#ff7d00', c5: '#ffecd1'
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
function getColorway(hexo, page) {
|
|
27
|
+
const pageColorway = page && page.colorway;
|
|
28
|
+
const globalColorway = hexo.config.design_cards && hexo.config.design_cards.colorway;
|
|
29
|
+
const key = pageColorway || globalColorway || 'olive-garden';
|
|
30
|
+
return colorways[key] || colorways['olive-garden'];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function tint(hex, opacity) {
|
|
34
|
+
opacity = opacity || 0.12;
|
|
35
|
+
const r = parseInt(hex.slice(1,3), 16);
|
|
36
|
+
const g = parseInt(hex.slice(3,5), 16);
|
|
37
|
+
const b = parseInt(hex.slice(5,7), 16);
|
|
38
|
+
return 'rgba(' + r + ',' + g + ',' + b + ',' + opacity + ')';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
module.exports = { colorways, getColorway, tint };
|
package/lib/css.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function getCSS() {
|
|
4
|
+
return `
|
|
5
|
+
<style data-hexo-design-cards>
|
|
6
|
+
.dc-flow{display:flex;align-items:center;justify-content:center;gap:12px;margin:20px 0;flex-wrap:wrap}
|
|
7
|
+
.dc-flow-step{color:#fff;padding:12px 20px;border-radius:8px;text-align:center}
|
|
8
|
+
.dc-flow-step b{display:block}
|
|
9
|
+
.dc-flow-step span{font-size:12px}
|
|
10
|
+
.dc-flow-step.dc-highlight{border-width:3px;border-style:solid}
|
|
11
|
+
.dc-flow-arrow{font-size:24px}
|
|
12
|
+
.dc-flow-caption{text-align:center;font-size:13px;margin:0 0 10px}
|
|
13
|
+
|
|
14
|
+
.dc-cards{display:grid;gap:20px;margin:25px 0}
|
|
15
|
+
.dc-card{border-width:2px;border-style:solid;border-radius:12px;overflow:hidden}
|
|
16
|
+
.dc-card-header{padding:12px 15px}
|
|
17
|
+
.dc-card-header h4{margin:0;color:#fff;font-size:18px}
|
|
18
|
+
.dc-card-body{padding:15px}
|
|
19
|
+
.dc-card-body p{font-size:15px;color:#555;margin:0 0 10px}
|
|
20
|
+
.dc-card-body code{background:var(--dc-c5);padding:2px 6px;border-radius:4px;font-size:13px}
|
|
21
|
+
|
|
22
|
+
.dc-accents{display:grid;gap:15px;margin:25px 0}
|
|
23
|
+
.dc-accent{padding:15px;border-radius:8px;border-left:5px solid}
|
|
24
|
+
.dc-accent b{display:block;margin-bottom:4px}
|
|
25
|
+
.dc-accent p{font-size:15px;margin:5px 0 0;color:#555}
|
|
26
|
+
|
|
27
|
+
.dc-compare{display:grid;grid-template-columns:1fr 1fr;gap:20px;margin:20px 0}
|
|
28
|
+
.dc-compare-option{padding:20px;border-radius:12px;border-width:3px;border-style:solid}
|
|
29
|
+
.dc-compare-option.dc-recommended{border-width:2px}
|
|
30
|
+
.dc-compare-option h4{margin:0 0 10px;text-align:center}
|
|
31
|
+
.dc-compare-option .dc-emoji{text-align:center;font-size:40px}
|
|
32
|
+
.dc-compare-body{text-align:center}
|
|
33
|
+
.dc-compare-body p{font-size:15px;color:#555;line-height:1.6}
|
|
34
|
+
.dc-compare-body p strong{font-size:24px;display:block;margin:10px 0}
|
|
35
|
+
|
|
36
|
+
.dc-quotes{border-width:3px;border-style:solid;border-radius:12px;padding:20px;margin:25px 0}
|
|
37
|
+
.dc-quotes h4{margin:0 0 15px}
|
|
38
|
+
.dc-quote{border-left:3px solid;padding-left:15px;margin-bottom:15px}
|
|
39
|
+
.dc-quote p{margin:0;font-size:14px;font-style:italic}
|
|
40
|
+
.dc-quote cite{display:block;margin:5px 0 0;font-size:12px;font-style:normal}
|
|
41
|
+
|
|
42
|
+
.dc-alert{border-width:2px;border-style:solid;border-radius:12px;padding:20px;margin:25px 0}
|
|
43
|
+
.dc-alert h4{margin:0 0 10px}
|
|
44
|
+
.dc-alert p,.dc-alert div{margin:0;line-height:1.8}
|
|
45
|
+
|
|
46
|
+
.dc-minicards{display:grid;grid-template-columns:repeat(3,1fr);gap:15px;margin:20px 0}
|
|
47
|
+
.dc-mini{border-width:3px;border-style:solid;border-radius:8px;padding:15px}
|
|
48
|
+
.dc-mini h5{margin:0 0 8px}
|
|
49
|
+
.dc-mini p{font-size:13px;color:#555;margin:0}
|
|
50
|
+
.dc-mini code{background:rgba(0,0,0,0.06);padding:1px 5px;border-radius:3px;font-size:0.85em}
|
|
51
|
+
|
|
52
|
+
.dc-banner{color:#fff;padding:15px 20px;border-radius:8px;margin:60px 0 20px}
|
|
53
|
+
.dc-banner h2{margin:0;font-size:20px;font-weight:bold}
|
|
54
|
+
|
|
55
|
+
@media(max-width:768px){
|
|
56
|
+
.dc-cards,.dc-accents{grid-template-columns:1fr!important}
|
|
57
|
+
.dc-compare{grid-template-columns:1fr}
|
|
58
|
+
.dc-minicards{grid-template-columns:1fr}
|
|
59
|
+
.dc-flow{flex-direction:column}
|
|
60
|
+
.dc-flow-arrow{transform:rotate(90deg)}
|
|
61
|
+
}
|
|
62
|
+
</style>`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
module.exports = { getCSS };
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hexo-design-cards",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Beautiful design card tags for Hexo — flow diagrams, header cards, accent cards, comparison cards, quotes, alerts, mini cards, and section banners with customizable colorways.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"hexo",
|
|
8
|
+
"hexo-plugin",
|
|
9
|
+
"hexo-tag",
|
|
10
|
+
"design",
|
|
11
|
+
"cards",
|
|
12
|
+
"colorway",
|
|
13
|
+
"diagram"
|
|
14
|
+
],
|
|
15
|
+
"author": "leafbird",
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/leafbird/hexo-design-cards.git"
|
|
20
|
+
},
|
|
21
|
+
"homepage": "https://github.com/leafbird/hexo-design-cards",
|
|
22
|
+
"directories": {
|
|
23
|
+
"lib": "lib"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"test": "mocha test/"
|
|
27
|
+
},
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/leafbird/hexo-design-cards/issues"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"mocha": "^11.7.5"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/tags/accents.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { getColorway, tint } = require('../lib/colorways');
|
|
3
|
+
|
|
4
|
+
module.exports = function(hexo) {
|
|
5
|
+
const colors_cycle = ['c3', 'c1', 'c4', 'c2'];
|
|
6
|
+
let accentIndex = 0;
|
|
7
|
+
let accentFontSize = null;
|
|
8
|
+
|
|
9
|
+
hexo.extend.tag.register('accents', function(args) {
|
|
10
|
+
const cols = parseInt(args[0]) || 2;
|
|
11
|
+
const lastArg = args[args.length - 1];
|
|
12
|
+
accentFontSize = (args.length > 1 && /^\d+$/.test(lastArg)) ? lastArg + 'px' : null;
|
|
13
|
+
accentIndex = 0;
|
|
14
|
+
return '<div class="dc-accents" style="grid-template-columns:repeat(' + cols + ',1fr)">';
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
hexo.extend.tag.register('endaccents', function() {
|
|
18
|
+
return '</div>';
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
hexo.extend.tag.register('accent', function(args, content) {
|
|
22
|
+
const cw = getColorway(hexo, this);
|
|
23
|
+
const title = args.join(' ').replace(/^"|"$/g, '');
|
|
24
|
+
const colorKey = colors_cycle[accentIndex % colors_cycle.length];
|
|
25
|
+
const color = cw[colorKey];
|
|
26
|
+
accentIndex++;
|
|
27
|
+
const rendered = hexo.render.renderSync({ text: content, engine: 'markdown' });
|
|
28
|
+
const fsStyle = accentFontSize ? ' style="font-size:' + accentFontSize + '"' : '';
|
|
29
|
+
return '<div class="dc-accent" style="background:' + tint(color, 0.1) + ';border-left-color:' + color + '">' +
|
|
30
|
+
'<b>' + title + '</b><div' + fsStyle + '>' + rendered + '</div></div>';
|
|
31
|
+
}, { ends: true });
|
|
32
|
+
};
|
package/tags/alert.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { getColorway, tint } = require('../lib/colorways');
|
|
3
|
+
|
|
4
|
+
module.exports = function(hexo) {
|
|
5
|
+
hexo.extend.tag.register('alert', function(args, content) {
|
|
6
|
+
const cw = getColorway(hexo, this);
|
|
7
|
+
const type = args[0] || 'info';
|
|
8
|
+
// Check if last arg is a number (font size)
|
|
9
|
+
const lastArg = args[args.length - 1];
|
|
10
|
+
const fontSize = (args.length > 1 && /^\d+$/.test(lastArg)) ? lastArg + 'px' : null;
|
|
11
|
+
|
|
12
|
+
const icons = { info: 'ℹ️', warning: '⚠️', tip: '💡' };
|
|
13
|
+
const colorMap = { info: cw.c3, warning: cw.c2, tip: cw.c4 };
|
|
14
|
+
const bgMap = { info: tint(cw.c3, 0.08), warning: tint(cw.c2, 0.08), tip: tint(cw.c4, 0.08) };
|
|
15
|
+
const color = colorMap[type] || cw.c3;
|
|
16
|
+
const bg = bgMap[type] || tint(cw.c3, 0.08);
|
|
17
|
+
const icon = icons[type] || 'ℹ️';
|
|
18
|
+
|
|
19
|
+
const rendered = content.trim();
|
|
20
|
+
const pipeIdx = rendered.indexOf('|');
|
|
21
|
+
let title = '', body = rendered;
|
|
22
|
+
if (pipeIdx > 0 && pipeIdx < 60) {
|
|
23
|
+
title = rendered.slice(0, pipeIdx).trim();
|
|
24
|
+
body = rendered.slice(pipeIdx + 1).trim();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const fsStyle = fontSize ? ' style="font-size:' + fontSize + '"' : '';
|
|
28
|
+
let html = '<div class="dc-alert" style="background:' + bg + ';border-color:' + color + '">';
|
|
29
|
+
if (title) html += '<h4 style="color:' + color + '">' + icon + ' ' + title + '</h4>';
|
|
30
|
+
else html += '<h4 style="color:' + color + '">' + icon + '</h4>';
|
|
31
|
+
html += '<div' + fsStyle + '>' + hexo.render.renderSync({ text: body, engine: 'markdown' }) + '</div>';
|
|
32
|
+
html += '</div>';
|
|
33
|
+
return html;
|
|
34
|
+
}, { ends: true });
|
|
35
|
+
};
|
package/tags/banner.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { getColorway } = require('../lib/colorways');
|
|
3
|
+
|
|
4
|
+
// {% banner "Section N: Title" %}
|
|
5
|
+
module.exports = function(hexo) {
|
|
6
|
+
hexo.extend.tag.register('banner', function(args) {
|
|
7
|
+
const cw = getColorway(hexo, this);
|
|
8
|
+
const text = args.join(' ').replace(/^"|"$/g, '');
|
|
9
|
+
return '<div class="dc-banner" style="background:' + cw.c1 + '"><h2>' + text + '</h2></div>';
|
|
10
|
+
});
|
|
11
|
+
};
|
package/tags/cards.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { getColorway } = require('../lib/colorways');
|
|
3
|
+
|
|
4
|
+
module.exports = function(hexo) {
|
|
5
|
+
const colors_cycle = ['c3', 'c2', 'c4', 'c1'];
|
|
6
|
+
let cardIndex = 0;
|
|
7
|
+
let cardFontSize = null;
|
|
8
|
+
|
|
9
|
+
hexo.extend.tag.register('cards', function(args) {
|
|
10
|
+
const cols = parseInt(args[0]) || 2;
|
|
11
|
+
const lastArg = args[args.length - 1];
|
|
12
|
+
cardFontSize = (args.length > 1 && /^\d+$/.test(lastArg)) ? lastArg + 'px' : null;
|
|
13
|
+
cardIndex = 0;
|
|
14
|
+
return '<div class="dc-cards" style="grid-template-columns:repeat(' + cols + ',1fr)">';
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
hexo.extend.tag.register('endcards', function() {
|
|
18
|
+
return '</div>';
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
hexo.extend.tag.register('card', function(args, content) {
|
|
22
|
+
const cw = getColorway(hexo, this);
|
|
23
|
+
const title = args.join(' ').replace(/^"|"$/g, '');
|
|
24
|
+
const colorKey = colors_cycle[cardIndex % colors_cycle.length];
|
|
25
|
+
const color = cw[colorKey];
|
|
26
|
+
cardIndex++;
|
|
27
|
+
const rendered = hexo.render.renderSync({ text: content, engine: 'markdown' });
|
|
28
|
+
const fsStyle = cardFontSize ? ' style="font-size:' + cardFontSize + '"' : '';
|
|
29
|
+
return '<div class="dc-card" style="border-color:' + color + '">' +
|
|
30
|
+
'<div class="dc-card-header" style="background:' + color + '"><h4>' + title + '</h4></div>' +
|
|
31
|
+
'<div class="dc-card-body"' + fsStyle + '>' + rendered + '</div></div>';
|
|
32
|
+
}, { ends: true });
|
|
33
|
+
};
|
package/tags/compare.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { getColorway, tint } = require('../lib/colorways');
|
|
3
|
+
|
|
4
|
+
module.exports = function(hexo) {
|
|
5
|
+
let optionIndex = 0;
|
|
6
|
+
let compareFontSize = null;
|
|
7
|
+
|
|
8
|
+
hexo.extend.tag.register('compare', function(args) {
|
|
9
|
+
optionIndex = 0;
|
|
10
|
+
const lastArg = args[args.length - 1];
|
|
11
|
+
compareFontSize = (args.length > 0 && /^\d+$/.test(lastArg)) ? lastArg + 'px' : null;
|
|
12
|
+
return '<div class="dc-compare">';
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
hexo.extend.tag.register('endcompare', function() {
|
|
16
|
+
return '</div>';
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
hexo.extend.tag.register('option', function(args, content) {
|
|
20
|
+
const cw = getColorway(hexo, this);
|
|
21
|
+
const title = args[0] || '';
|
|
22
|
+
const emoji = args[1] || '';
|
|
23
|
+
const recommended = args.indexOf('recommended') > -1;
|
|
24
|
+
const colors = [cw.c3, cw.c2, cw.c1, cw.c4];
|
|
25
|
+
const color = recommended ? cw.c3 : colors[optionIndex % colors.length];
|
|
26
|
+
const bg = tint(color, 0.08);
|
|
27
|
+
optionIndex++;
|
|
28
|
+
const rendered = hexo.render.renderSync({ text: content, engine: 'markdown' });
|
|
29
|
+
const fsStyle = compareFontSize ? ' style="font-size:' + compareFontSize + '"' : '';
|
|
30
|
+
return '<div class="dc-compare-option' + (recommended ? ' dc-recommended' : '') +
|
|
31
|
+
'" style="background:' + bg + ';border-color:' + color + '">' +
|
|
32
|
+
'<h4 style="color:' + color + '">' + title + '</h4>' +
|
|
33
|
+
(emoji ? '<div class="dc-emoji">' + emoji + '</div>' : '') +
|
|
34
|
+
'<div class="dc-compare-body"' + fsStyle + '>' + rendered + '</div></div>';
|
|
35
|
+
}, { ends: true });
|
|
36
|
+
};
|
package/tags/flow.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { getColorway } = require('../lib/colorways');
|
|
3
|
+
|
|
4
|
+
// {% flow "Step A|desc" "*Step B|desc" "Step C|desc" %}
|
|
5
|
+
// {% flow "Step A|desc" "*Step B|desc" "Step C|desc" | caption %}
|
|
6
|
+
module.exports = function(hexo) {
|
|
7
|
+
hexo.extend.tag.register('flow', function(args) {
|
|
8
|
+
const cw = getColorway(hexo, this);
|
|
9
|
+
|
|
10
|
+
// Hexo strips quotes and delivers each quoted group as one element in args[].
|
|
11
|
+
// Find caption: look for a bare "|" separator in args
|
|
12
|
+
let caption = '';
|
|
13
|
+
let stepArgs = args;
|
|
14
|
+
const pipeIdx = args.indexOf('|');
|
|
15
|
+
if (pipeIdx > 0) {
|
|
16
|
+
caption = args.slice(pipeIdx + 1).join(' ');
|
|
17
|
+
stepArgs = args.slice(0, pipeIdx);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Each arg is a step: "title|desc" or "*title|desc"
|
|
21
|
+
const steps = stepArgs;
|
|
22
|
+
const colors = [cw.c3, cw.c2, cw.c1, cw.c4];
|
|
23
|
+
let html = '<div class="dc-flow">';
|
|
24
|
+
steps.forEach(function(step, i) {
|
|
25
|
+
if (i > 0) {
|
|
26
|
+
html += '<span class="dc-flow-arrow" style="color:' + cw.c4 + '">→</span>';
|
|
27
|
+
}
|
|
28
|
+
const highlight = step.startsWith('*');
|
|
29
|
+
const text = highlight ? step.slice(1) : step;
|
|
30
|
+
const parts = text.split('|');
|
|
31
|
+
const title = parts[0];
|
|
32
|
+
const desc = parts[1] || '';
|
|
33
|
+
const bg = highlight ? cw.c1 : colors[i % colors.length];
|
|
34
|
+
const borderStyle = highlight ? ';border:3px solid ' + cw.c4 : '';
|
|
35
|
+
html += '<div class="dc-flow-step' + (highlight ? ' dc-highlight' : '') + '" style="background:' + bg + borderStyle + '">';
|
|
36
|
+
html += '<b>' + title + '</b>';
|
|
37
|
+
if (desc) html += '<span>' + desc + '</span>';
|
|
38
|
+
html += '</div>';
|
|
39
|
+
});
|
|
40
|
+
html += '</div>';
|
|
41
|
+
if (caption) {
|
|
42
|
+
html += '<p class="dc-flow-caption" style="color:' + cw.c4 + '">' + caption + '</p>';
|
|
43
|
+
}
|
|
44
|
+
return html;
|
|
45
|
+
});
|
|
46
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { getColorway } = require('../lib/colorways');
|
|
3
|
+
|
|
4
|
+
module.exports = function(hexo) {
|
|
5
|
+
const colors_cycle = ['c1', 'c2', 'c3', 'c4'];
|
|
6
|
+
let miniIndex = 0;
|
|
7
|
+
|
|
8
|
+
hexo.extend.tag.register('minicards', function() {
|
|
9
|
+
miniIndex = 0;
|
|
10
|
+
return '<div class="dc-minicards">';
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
hexo.extend.tag.register('endminicards', function() {
|
|
14
|
+
return '</div>';
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
hexo.extend.tag.register('mini', function(args, content) {
|
|
18
|
+
const cw = getColorway(hexo, this);
|
|
19
|
+
const title = args.join(' ').replace(/^"|"$/g, '');
|
|
20
|
+
const colorKey = colors_cycle[miniIndex % colors_cycle.length];
|
|
21
|
+
miniIndex++;
|
|
22
|
+
const rendered = hexo.render.renderSync({ text: content, engine: 'markdown' });
|
|
23
|
+
return '<div class="dc-mini" style="border-color:' + cw[colorKey] + ';background:' + cw.c5 + '">' +
|
|
24
|
+
'<h5 style="color:' + cw.c2 + '">' + title + '</h5>' +
|
|
25
|
+
rendered + '</div>';
|
|
26
|
+
}, { ends: true });
|
|
27
|
+
};
|
package/tags/quotes.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { getColorway } = require('../lib/colorways');
|
|
3
|
+
|
|
4
|
+
module.exports = function(hexo) {
|
|
5
|
+
const colors_cycle = ['c3', 'c2', 'c4', 'c1'];
|
|
6
|
+
let quoteIndex = 0;
|
|
7
|
+
|
|
8
|
+
hexo.extend.tag.register('quotes', function(args) {
|
|
9
|
+
const cw = getColorway(hexo, this);
|
|
10
|
+
const title = args.join(' ').replace(/^"|"$/g, '');
|
|
11
|
+
quoteIndex = 0;
|
|
12
|
+
let html = '<div class="dc-quotes" style="background:' + cw.c5 + ';border-color:' + cw.c4 + '">';
|
|
13
|
+
if (title) html += '<h4 style="color:' + cw.c1 + '">' + title + '</h4>';
|
|
14
|
+
return html;
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
hexo.extend.tag.register('endquotes', function() {
|
|
18
|
+
return '</div>';
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
hexo.extend.tag.register('dcquote', function(args, content) {
|
|
22
|
+
const cw = getColorway(hexo, this);
|
|
23
|
+
const source = args.join(' ').replace(/^"|"$/g, '');
|
|
24
|
+
const colorKey = colors_cycle[quoteIndex % colors_cycle.length];
|
|
25
|
+
quoteIndex++;
|
|
26
|
+
return '<div class="dc-quote" style="border-left-color:' + cw[colorKey] + '">' +
|
|
27
|
+
'<p>' + content.trim() + '</p>' +
|
|
28
|
+
'<cite style="color:' + cw.c4 + '">— ' + source + '</cite></div>';
|
|
29
|
+
}, { ends: true });
|
|
30
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Minimal Hexo mock for testing tag plugins.
|
|
5
|
+
*/
|
|
6
|
+
function createMockHexo(options) {
|
|
7
|
+
options = options || {};
|
|
8
|
+
const tags = {};
|
|
9
|
+
|
|
10
|
+
const hexo = {
|
|
11
|
+
config: options.config || {},
|
|
12
|
+
extend: {
|
|
13
|
+
tag: {
|
|
14
|
+
register: function(name, fn, opts) {
|
|
15
|
+
tags[name] = { fn: fn, opts: opts || {} };
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
filter: {
|
|
19
|
+
register: function() {}
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
render: {
|
|
23
|
+
renderSync: function(data) {
|
|
24
|
+
// Simple passthrough — wrap in <p> to mimic markdown
|
|
25
|
+
return '<p>' + (data.text || '').trim() + '</p>';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
function callTag(name, args, content) {
|
|
31
|
+
const tag = tags[name];
|
|
32
|
+
if (!tag) throw new Error('Tag not registered: ' + name);
|
|
33
|
+
var ctx = options.page || {};
|
|
34
|
+
if (tag.opts.ends) {
|
|
35
|
+
return tag.fn.call(ctx, args, content || '');
|
|
36
|
+
}
|
|
37
|
+
return tag.fn.call(ctx, args);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return { hexo: hexo, tags: tags, callTag: callTag };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = { createMockHexo: createMockHexo };
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const assert = require('assert');
|
|
3
|
+
const { createMockHexo } = require('./mock-hexo');
|
|
4
|
+
|
|
5
|
+
describe('hexo-design-cards tags', function() {
|
|
6
|
+
|
|
7
|
+
describe('banner', function() {
|
|
8
|
+
it('should render a banner with title', function() {
|
|
9
|
+
const mock = createMockHexo();
|
|
10
|
+
require('../tags/banner')(mock.hexo);
|
|
11
|
+
const html = mock.callTag('banner', ['Getting Started']);
|
|
12
|
+
assert(html.includes('dc-banner'));
|
|
13
|
+
assert(html.includes('Getting Started'));
|
|
14
|
+
assert(html.includes('<h2>'));
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe('cards', function() {
|
|
19
|
+
it('should render cards container with correct columns', function() {
|
|
20
|
+
const mock = createMockHexo();
|
|
21
|
+
require('../tags/cards')(mock.hexo);
|
|
22
|
+
const open = mock.callTag('cards', ['3']);
|
|
23
|
+
assert(open.includes('repeat(3,1fr)'));
|
|
24
|
+
const close = mock.callTag('endcards', []);
|
|
25
|
+
assert.strictEqual(close, '</div>');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should render a card with title and content', function() {
|
|
29
|
+
const mock = createMockHexo();
|
|
30
|
+
require('../tags/cards')(mock.hexo);
|
|
31
|
+
mock.callTag('cards', ['2']);
|
|
32
|
+
const html = mock.callTag('card', ['Title A'], 'Some content');
|
|
33
|
+
assert(html.includes('dc-card'));
|
|
34
|
+
assert(html.includes('dc-card-header'));
|
|
35
|
+
assert(html.includes('Title A'));
|
|
36
|
+
assert(html.includes('Some content'));
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should apply font size when specified', function() {
|
|
40
|
+
const mock = createMockHexo();
|
|
41
|
+
require('../tags/cards')(mock.hexo);
|
|
42
|
+
mock.callTag('cards', ['2', '14']);
|
|
43
|
+
const html = mock.callTag('card', ['Title'], 'Content');
|
|
44
|
+
assert(html.includes('font-size:14px'));
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe('accents', function() {
|
|
49
|
+
it('should render accents container', function() {
|
|
50
|
+
const mock = createMockHexo();
|
|
51
|
+
require('../tags/accents')(mock.hexo);
|
|
52
|
+
const open = mock.callTag('accents', ['2']);
|
|
53
|
+
assert(open.includes('dc-accents'));
|
|
54
|
+
assert(open.includes('repeat(2,1fr)'));
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should render accent card with title', function() {
|
|
58
|
+
const mock = createMockHexo();
|
|
59
|
+
require('../tags/accents')(mock.hexo);
|
|
60
|
+
mock.callTag('accents', ['2']);
|
|
61
|
+
const html = mock.callTag('accent', ['Point 1'], 'Description');
|
|
62
|
+
assert(html.includes('dc-accent'));
|
|
63
|
+
assert(html.includes('Point 1'));
|
|
64
|
+
assert(html.includes('Description'));
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe('compare', function() {
|
|
69
|
+
it('should render compare container', function() {
|
|
70
|
+
const mock = createMockHexo();
|
|
71
|
+
require('../tags/compare')(mock.hexo);
|
|
72
|
+
const open = mock.callTag('compare', []);
|
|
73
|
+
assert(open.includes('dc-compare'));
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should render option with emoji and recommended', function() {
|
|
77
|
+
const mock = createMockHexo();
|
|
78
|
+
require('../tags/compare')(mock.hexo);
|
|
79
|
+
const html = mock.callTag('option', ['Option B', '🚀', 'recommended'], 'Best choice');
|
|
80
|
+
assert(html.includes('dc-compare-option'));
|
|
81
|
+
assert(html.includes('🚀'));
|
|
82
|
+
assert(html.includes('Option B'));
|
|
83
|
+
assert(html.includes('Best choice'));
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('alert', function() {
|
|
88
|
+
it('should render info alert', function() {
|
|
89
|
+
const mock = createMockHexo();
|
|
90
|
+
require('../tags/alert')(mock.hexo);
|
|
91
|
+
const html = mock.callTag('alert', ['info'], 'Title|Body text');
|
|
92
|
+
assert(html.includes('dc-alert'));
|
|
93
|
+
assert(html.includes('Title'));
|
|
94
|
+
assert(html.includes('Body text'));
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should render warning alert', function() {
|
|
98
|
+
const mock = createMockHexo();
|
|
99
|
+
require('../tags/alert')(mock.hexo);
|
|
100
|
+
const html = mock.callTag('alert', ['warning'], 'Warn|Be careful');
|
|
101
|
+
assert(html.includes('dc-alert'));
|
|
102
|
+
assert(html.includes('⚠️'));
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should render tip alert', function() {
|
|
106
|
+
const mock = createMockHexo();
|
|
107
|
+
require('../tags/alert')(mock.hexo);
|
|
108
|
+
const html = mock.callTag('alert', ['tip'], 'Tip|Useful info');
|
|
109
|
+
assert(html.includes('dc-alert'));
|
|
110
|
+
assert(html.includes('💡'));
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
describe('quotes', function() {
|
|
115
|
+
it('should render quotes container with title', function() {
|
|
116
|
+
const mock = createMockHexo();
|
|
117
|
+
require('../tags/quotes')(mock.hexo);
|
|
118
|
+
const open = mock.callTag('quotes', ['Famous Quotes']);
|
|
119
|
+
assert(open.includes('dc-quotes'));
|
|
120
|
+
assert(open.includes('Famous Quotes'));
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('should render dcquote with source', function() {
|
|
124
|
+
const mock = createMockHexo();
|
|
125
|
+
require('../tags/quotes')(mock.hexo);
|
|
126
|
+
const html = mock.callTag('dcquote', ['Author'], 'Quote text');
|
|
127
|
+
assert(html.includes('dc-quote'));
|
|
128
|
+
assert(html.includes('Author'));
|
|
129
|
+
assert(html.includes('Quote text'));
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe('minicards', function() {
|
|
134
|
+
it('should render minicards container', function() {
|
|
135
|
+
const mock = createMockHexo();
|
|
136
|
+
require('../tags/minicards')(mock.hexo);
|
|
137
|
+
const open = mock.callTag('minicards', []);
|
|
138
|
+
assert(open.includes('dc-minicards'));
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('should render mini card with title', function() {
|
|
142
|
+
const mock = createMockHexo();
|
|
143
|
+
require('../tags/minicards')(mock.hexo);
|
|
144
|
+
const html = mock.callTag('mini', ['Item A'], 'Short desc');
|
|
145
|
+
assert(html.includes('dc-mini'));
|
|
146
|
+
assert(html.includes('Item A'));
|
|
147
|
+
assert(html.includes('Short desc'));
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
describe('flow', function() {
|
|
152
|
+
it('should render flow steps', function() {
|
|
153
|
+
const mock = createMockHexo();
|
|
154
|
+
require('../tags/flow')(mock.hexo);
|
|
155
|
+
const html = mock.callTag('flow', ['Step A', '*Step B', 'Step C']);
|
|
156
|
+
assert(html.includes('dc-flow'));
|
|
157
|
+
assert(html.includes('dc-flow-step'));
|
|
158
|
+
assert(html.includes('Step A'));
|
|
159
|
+
assert(html.includes('Step B'));
|
|
160
|
+
assert(html.includes('Step C'));
|
|
161
|
+
assert(html.includes('dc-flow-arrow'));
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('should highlight step with * prefix', function() {
|
|
165
|
+
const mock = createMockHexo();
|
|
166
|
+
require('../tags/flow')(mock.hexo);
|
|
167
|
+
const html = mock.callTag('flow', ['A', '*B', 'C']);
|
|
168
|
+
assert(html.includes('dc-highlight'));
|
|
169
|
+
// Should not show the * in the output
|
|
170
|
+
assert(!html.includes('>*B<'));
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('should render step description with pipe separator', function() {
|
|
174
|
+
const mock = createMockHexo();
|
|
175
|
+
require('../tags/flow')(mock.hexo);
|
|
176
|
+
const html = mock.callTag('flow', ['Draft|Write it', '*Build|hexo generate', 'Deploy|Push']);
|
|
177
|
+
assert(html.includes('Draft'));
|
|
178
|
+
assert(html.includes('Write it'));
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('should render caption after bare pipe', function() {
|
|
182
|
+
const mock = createMockHexo();
|
|
183
|
+
require('../tags/flow')(mock.hexo);
|
|
184
|
+
const html = mock.callTag('flow', ['Request', '*Process', 'Response', '|', 'Data flow overview']);
|
|
185
|
+
assert(html.includes('dc-flow-caption'));
|
|
186
|
+
assert(html.includes('Data flow overview'));
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
describe('colorways', function() {
|
|
191
|
+
it('should default to olive-garden', function() {
|
|
192
|
+
const { getColorway } = require('../lib/colorways');
|
|
193
|
+
const hexo = { config: {} };
|
|
194
|
+
const cw = getColorway(hexo, {});
|
|
195
|
+
assert.strictEqual(cw.c1, '#283618');
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('should respect page colorway override', function() {
|
|
199
|
+
const { getColorway } = require('../lib/colorways');
|
|
200
|
+
const hexo = { config: {} };
|
|
201
|
+
const cw = getColorway(hexo, { colorway: 'fiery-ocean' });
|
|
202
|
+
assert.strictEqual(cw.c1, '#780000');
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('should fall back to olive-garden for unknown colorway', function() {
|
|
206
|
+
const { getColorway } = require('../lib/colorways');
|
|
207
|
+
const hexo = { config: {} };
|
|
208
|
+
const cw = getColorway(hexo, { colorway: 'nonexistent' });
|
|
209
|
+
assert.strictEqual(cw.c1, '#283618');
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
describe('CSS injection', function() {
|
|
214
|
+
it('should generate CSS with all tag classes', function() {
|
|
215
|
+
const { getCSS } = require('../lib/css');
|
|
216
|
+
const css = getCSS();
|
|
217
|
+
assert(css.includes('dc-flow'));
|
|
218
|
+
assert(css.includes('dc-cards'));
|
|
219
|
+
assert(css.includes('dc-accent'));
|
|
220
|
+
assert(css.includes('dc-compare'));
|
|
221
|
+
assert(css.includes('dc-alert'));
|
|
222
|
+
assert(css.includes('dc-quotes'));
|
|
223
|
+
assert(css.includes('dc-mini'));
|
|
224
|
+
assert(css.includes('dc-banner'));
|
|
225
|
+
assert(css.includes('data-hexo-design-cards'));
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
});
|