justskill-design 0.0.1 → 3.2.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.
Files changed (67) hide show
  1. package/README.en.md +235 -0
  2. package/README.md +184 -11
  3. package/SKILL.md +435 -0
  4. package/canvas/app.mjs +2870 -0
  5. package/canvas/groups.mjs +204 -0
  6. package/canvas/index.html +141 -0
  7. package/canvas/styles.css +1032 -0
  8. package/canvas/tree.mjs +249 -0
  9. package/cordis.patch.yml +8 -0
  10. package/design.mjs +172 -0
  11. package/docs/dsh-workbench.png +0 -0
  12. package/dsh/client.js +864 -0
  13. package/dsh/index.js +354 -0
  14. package/dsh/scan.js +139 -0
  15. package/dsh/skill.js +41 -0
  16. package/lib/base-css.mjs +92 -0
  17. package/lib/detect.mjs +123 -0
  18. package/lib/diagnostics.mjs +382 -0
  19. package/lib/export-html.mjs +309 -0
  20. package/lib/export-svg.mjs +111 -0
  21. package/lib/fragment.mjs +702 -0
  22. package/lib/geometry.mjs +827 -0
  23. package/lib/icons.mjs +106 -0
  24. package/lib/levenshtein.mjs +133 -0
  25. package/lib/node/cli.mjs +387 -0
  26. package/lib/node/export-svg.mjs +75 -0
  27. package/lib/node/fs.mjs +309 -0
  28. package/lib/node/op-json.mjs +75 -0
  29. package/lib/node/raster-run.mjs +115 -0
  30. package/lib/node/server.mjs +1264 -0
  31. package/lib/node/static.mjs +206 -0
  32. package/lib/node/verbs/add.mjs +220 -0
  33. package/lib/node/verbs/export.mjs +161 -0
  34. package/lib/node/verbs/format.mjs +106 -0
  35. package/lib/node/verbs/import.mjs +126 -0
  36. package/lib/node/verbs/ls.mjs +98 -0
  37. package/lib/node/verbs/migrate.mjs +236 -0
  38. package/lib/node/verbs/move.mjs +76 -0
  39. package/lib/node/verbs/new.mjs +284 -0
  40. package/lib/node/verbs/remove.mjs +41 -0
  41. package/lib/node/verbs/rename.mjs +87 -0
  42. package/lib/node/verbs/serve.mjs +164 -0
  43. package/lib/node/verbs/set.mjs +150 -0
  44. package/lib/node/verbs/show.mjs +122 -0
  45. package/lib/node/verbs/tokens.mjs +167 -0
  46. package/lib/node/verbs/validate.mjs +130 -0
  47. package/lib/node/write-verbs.mjs +215 -0
  48. package/lib/normalize.mjs +288 -0
  49. package/lib/ops.mjs +2063 -0
  50. package/lib/parse.mjs +997 -0
  51. package/lib/raster.mjs +186 -0
  52. package/lib/readers/pen.mjs +1684 -0
  53. package/lib/receipt.mjs +203 -0
  54. package/lib/render.mjs +503 -0
  55. package/lib/serialize.mjs +441 -0
  56. package/lib/spans.mjs +177 -0
  57. package/lib/svg-path.mjs +483 -0
  58. package/lib/table.mjs +1047 -0
  59. package/lib/tokens.mjs +295 -0
  60. package/lib/validate.mjs +929 -0
  61. package/package.json +68 -6
  62. package/references/cookbook.md +156 -0
  63. package/references/images.md +171 -0
  64. package/references/product-flow.md +408 -0
  65. package/references/schema.md +90 -0
  66. package/references/styling.md +181 -0
  67. package/scripts/install.mjs +95 -0
package/package.json CHANGED
@@ -1,10 +1,72 @@
1
1
  {
2
2
  "name": "justskill-design",
3
- "version": "0.0.1",
4
- "description": "Name reserved for justSkill — design web pages as a validated HTML file with inline CSS. The skill lives at https://github.com/justskill-ai/justskill-design",
3
+ "version": "3.2.0",
4
+ "private": false,
5
+ "type": "module",
6
+ "description": "Design web pages as a validated HTML file with inline CSS. Agent edits by CLI, human edits on a local canvas, both write the same file. Also ships a DeepSeek Harness plugin.",
5
7
  "license": "MIT",
6
- "repository": { "type": "git", "url": "https://github.com/justskill-ai/justskill-design.git" },
7
- "homepage": "https://github.com/justskill-ai/justskill-design",
8
- "keywords": ["justskill", "design", "html", "css", "canvas", "agent-skills", "claude-code"],
9
- "publishConfig": { "access": "public" }
8
+ "main": "./dsh/index.js",
9
+ "exports": {
10
+ ".": "./dsh/index.js",
11
+ "./client": "./dsh/client.js",
12
+ "./design.mjs": "./design.mjs",
13
+ "./package.json": "./package.json"
14
+ },
15
+ "bin": {
16
+ "justskill": "design.mjs"
17
+ },
18
+ "files": [
19
+ "SKILL.md",
20
+ "design.mjs",
21
+ "lib",
22
+ "canvas",
23
+ "references",
24
+ "scripts/install.mjs",
25
+ "dsh",
26
+ "cordis.patch.yml",
27
+ "README.md",
28
+ "LICENSE",
29
+ "README.en.md",
30
+ "docs/dsh-workbench.png"
31
+ ],
32
+ "dsh": {
33
+ "bundle": {
34
+ "patch": "./cordis.patch.yml"
35
+ },
36
+ "client": {
37
+ "platform": "web"
38
+ }
39
+ },
40
+ "keywords": [
41
+ "justskill",
42
+ "design",
43
+ "html",
44
+ "css",
45
+ "canvas",
46
+ "agent-skills",
47
+ "claude-code",
48
+ "dsh",
49
+ "dsh-plugin",
50
+ "deepseek-harness",
51
+ "skill"
52
+ ],
53
+ "engines": {
54
+ "node": ">=18",
55
+ "dsh": ">=0.2.1-rc.3 <0.3.0-0"
56
+ },
57
+ "scripts": {
58
+ "test": "node --test",
59
+ "references": "node scripts/gen-references.mjs",
60
+ "references:check": "node scripts/gen-references.mjs --check",
61
+ "images:check": "node scripts/check-images.mjs",
62
+ "install:skill": "node scripts/install.mjs"
63
+ },
64
+ "repository": {
65
+ "type": "git",
66
+ "url": "git+https://github.com/justskill-ai/justskill-design.git"
67
+ },
68
+ "homepage": "https://github.com/justskill-ai/justskill-design#readme",
69
+ "bugs": {
70
+ "url": "https://github.com/justskill-ai/justskill-design/issues"
71
+ }
10
72
  }
@@ -0,0 +1,156 @@
1
+ # Cookbook
2
+
3
+ <!-- Generated by scripts/gen-references.mjs from lib/table.mjs. Do not edit. -->
4
+
5
+ The patterns that come up on every page, written the way this format wants them.
6
+
7
+ ## Page shell
8
+
9
+ ```html
10
+ <section data-id="home" data-type="page" data-name="Home" data-canvas="0,0" style="width:390px;height:844px">
11
+ <div data-id="home__root" data-type="frame" style="display:flex;flex-direction:column;width:100%;height:100%;background-color:var(--color-bg)">
12
+ </div>
13
+ </section>
14
+ ```
15
+
16
+ The page owns its own size. The root fills it.
17
+
18
+ ## A vertical stack with spacing
19
+
20
+ ```html
21
+ <div data-id="home__list" data-type="frame" style="display:flex;flex-direction:column;gap:12px;padding:24px">
22
+ </div>
23
+ ```
24
+
25
+ `gap` is the whole spacing system. There is no `margin`.
26
+
27
+ ## A row that pushes its ends apart
28
+
29
+ ```html
30
+ <div data-id="home__bar" data-type="frame" style="display:flex;align-items:center;justify-content:space-between;height:48px;padding:0 16px;background-color:#ffffff">
31
+ </div>
32
+ ```
33
+
34
+ ## Two children that split the width
35
+
36
+ ```html
37
+ <div data-id="home__split" data-type="frame" style="display:flex;flex-direction:row;gap:8px">
38
+ <div data-id="home__left" data-type="frame" style="flex:1 1 0%;min-width:0"></div>
39
+ <div data-id="home__right" data-type="frame" style="flex:1 1 0%;min-width:0"></div>
40
+ </div>
41
+ ```
42
+
43
+ `flex:1 1 0%` plus `min-width:0` is the one spelled way to fill a row. Without
44
+ `min-width:0`, a long word pushes the child past its share.
45
+
46
+ ## A card
47
+
48
+ ```html
49
+ <div data-id="home__card" data-type="frame" style="display:flex;flex-direction:column;gap:8px;padding:16px;background-color:#ffffff;border:1px solid #e5e7eb;border-radius:12px;box-shadow:0 1px 2px #00000014">
50
+ </div>
51
+ ```
52
+
53
+ ## A tilted card
54
+
55
+ ```html
56
+ <div data-id="home__card" data-type="frame" style="transform:rotate(-1.5deg);display:flex;flex-direction:column;gap:8px;padding:16px;background-color:#ffffff;border-radius:16px;box-shadow:0 1px 2px #0000000a, 0 16px 34px -12px #00000014"></div>
57
+ ```
58
+
59
+ `rotate()` turns the element about its own centre and changes no layout: its
60
+ neighbours stay put, and the canvas drags and resizes it along its turned
61
+ axes. `translate()` and `scale()` are refused — they are `left`/`top` and
62
+ `width`/`height` written a second time. A page cannot be rotated; tilt the
63
+ cards on it.
64
+
65
+ ## Text sizes
66
+
67
+ | Role | Style |
68
+ |---|---|
69
+ | Display | `font-size:44px;font-weight:700;line-height:1.1` |
70
+ | Title | `font-size:24px;font-weight:600;line-height:1.25` |
71
+ | Body | `font-size:16px;font-weight:400;line-height:1.5` |
72
+ | Label | `font-size:14px;font-weight:500;line-height:1.4` |
73
+ | Caption | `font-size:12px;font-weight:400;line-height:1.4;color:#6b7280` |
74
+
75
+ ## A pill
76
+
77
+ ```html
78
+ <p data-id="home__tag" data-type="text" style="font-size:12px;font-weight:500;padding:4px 10px;background-color:#f3f4f6;border-radius:9999px;color:#374151">New</p>
79
+ ```
80
+
81
+ `border-radius:9999px` is the pill. A circle is `border-radius:50%` on a square box.
82
+
83
+ ## An image that fills its box
84
+
85
+ ```html
86
+ <img data-id="home__photo" data-type="image" src="photo.jpg" alt="A room" style="width:100%;height:200px;object-fit:cover;border-radius:12px">
87
+ ```
88
+
89
+ `alt` is required. `alt=""` marks a decorative image, and that is different from
90
+ omitting it.
91
+
92
+ ## A photo when the user gave none
93
+
94
+ Take the `src` from `references/images.md`, and write its `alt` and credit with it:
95
+
96
+ ```html
97
+ <img data-id="home__photo" data-type="image" src="https://images.unsplash.com/photo-1543988884-c01cfa7b41c2?w=1080&amp;q=80&amp;fm=jpg" alt="Turquoise water meeting a white sand beach" data-credit="Photo by Harry Holder on Unsplash" style="width:100%;height:220px;object-fit:cover;border-radius:12px">
98
+ ```
99
+
100
+ `data-credit` names the photographer. It never renders, and the Licence does not
101
+ require it: keeping it costs one attribute and makes the source of a photo findable
102
+ later.
103
+
104
+ ## A background image
105
+
106
+ ```html
107
+ <div data-id="home__hero" data-type="frame" style="height:240px;background-image:url(&quot;hero.jpg&quot;);background-size:cover;background-position:center"></div>
108
+ ```
109
+
110
+ ## A gradient scrim
111
+
112
+ ```html
113
+ <div data-id="home__scrim" data-type="frame" style="height:120px;background-image:linear-gradient(180deg,#ffffff00,#00000099)"></div>
114
+ ```
115
+
116
+ A linear gradient's opaque stops are read by the contrast checker. A radial
117
+ gradient is accepted as a value, but the checker cannot see through it, so text
118
+ over a radial gradient is judged against the surface below it.
119
+
120
+ ## A dot grid
121
+
122
+ ```html
123
+ <div data-id="home__dots" data-type="frame" style="width:100%;height:100%;background-image:radial-gradient(circle, #e3e3e3 1px, #00000000 1.5px);background-size:26px 26px"></div>
124
+ ```
125
+
126
+ `background-size` takes a pair, so a repeatable texture is a value, not an
127
+ asset: the gradient draws one dot, and the size tiles it. The image repeats by
128
+ default, so no `background-repeat` is needed or available.
129
+
130
+ ## Glass behind a floating bar
131
+
132
+ ```html
133
+ <div data-id="home__bar" data-type="frame" style="position:absolute;left:16px;top:16px;padding:8px 12px;background-color:#ffffffb8;border:1px solid #ffffffcc;border-radius:999px;backdrop-filter:blur(20px) saturate(1.4)"></div>
134
+ ```
135
+
136
+ `blur()` and `saturate()` only, in that order. The surface colour stays
137
+ translucent: the filter blurs what is behind the element, it does not supply a
138
+ background of its own. A renderer that ignores the filter still draws the
139
+ translucent fill, which is why the fill is written too.
140
+
141
+ ## The most common refusals
142
+
143
+ | Written | Refused because | Write this |
144
+ |---|---|---|
145
+ | `animation` | No animation. A design file is a still. | remove it |
146
+ | `border-bottom` | Use border-width with four values for one edge. | `border-width: 0 0 1px 0` |
147
+ | `border-left` | Use border-width with four values for one edge. | `border-width: 0 0 0 1px` |
148
+ | `border-right` | Use border-width with four values for one edge. | `border-width: 0 1px 0 0` |
149
+ | `border-top` | Use border-width with four values for one edge. | `border-width: 1px 0 0 0` |
150
+ | `cursor` | The format describes appearance, not interaction. A cursor is behavior. | remove it |
151
+ | `font` | Write the longhand properties. | `font-family, font-size, font-weight and line-height` |
152
+ | `grid-gap` | The property is named gap. | `gap` |
153
+ | `grid-template-columns` | The format has no grid. Flex covers the same layouts. | `display:flex with flex:1 on the children` |
154
+ | `grid-template-rows` | The format has no grid. Flex covers the same layouts. | `display:flex with flex:1 on the children` |
155
+ | `margin-bottom` | Write the shorthand: margin: 0 0 8px 0. | `gap on the parent, or margin on this element` |
156
+ | `margin-left` | Write the shorthand: margin: 0 0 0 8px. | `gap on the parent, or margin on this element` |
@@ -0,0 +1,171 @@
1
+ # Image catalogue
2
+
3
+ Unsplash photographs for a page that needs a picture and has not been told which
4
+ one to use. Grouped by what the page is looking for; every URL was requested from
5
+ `images.unsplash.com` and returned a 200 on 2026-10-25.
6
+
7
+ <!-- Hand-written data, not generated. scripts/check-images.mjs re-checks the URLs. -->
8
+
9
+ ## How to use it
10
+
11
+ - **The user's image wins.** A URL, a file or a `.pen` image the user supplied is never
12
+ replaced. This catalogue is for the case where nobody said which picture to use.
13
+ - **Choose by subject, not by the nearest row.** A hero wants a wide landscape, a
14
+ feature card an object, an avatar a face, a testimonial one person.
15
+ - **Copy the `src` whole.** `?w=1080&q=80&fm=jpg` is part of the URL that was verified.
16
+ The `&` needs no escaping in the fragment you hand to `add`; the file is written with
17
+ `&amp;`.
18
+ - **A small avatar may ask for less:** change `w=1080` to `w=400` and leave the rest
19
+ of the URL alone.
20
+ - **The credit goes in `data-credit`.** It never renders. The Licence does not require
21
+ it; keeping it makes a photo findable again the day the URL breaks.
22
+ - **No photo fits?** A solid colour or a linear gradient is the honest placeholder.
23
+ Never invent a `photo-…` id: a URL that resolves to nothing renders an empty box,
24
+ which is worse than a placeholder nobody mistakes for a photograph.
25
+
26
+ ## Landscapes and scenery
27
+
28
+ Wide and tall scenes for heroes, cards and full-bleed backgrounds.
29
+
30
+ | src | Shows | Credit |
31
+ |---|---|---|
32
+ | `https://images.unsplash.com/photo-1634879036244-18870f8b804f?w=1080&q=80&fm=jpg` | Green meadow below snow-capped peaks at golden hour | Photo by Greg Š on Unsplash |
33
+ | `https://images.unsplash.com/photo-1542273917363-3b1817f69a2d?w=1080&q=80&fm=jpg` | Dense pine forest in thick morning fog | Photo by Marita Kavelashvili on Unsplash |
34
+ | `https://images.unsplash.com/photo-1696190288876-a2b40fcc63a9?w=1080&q=80&fm=jpg` | Dead tree stumps standing in a calm forest lake | Photo by Brett Mayson on Unsplash |
35
+ | `https://images.unsplash.com/photo-1638732984528-f255c9714619?w=1080&q=80&fm=jpg` | Smooth desert dunes fading into haze | Photo by raouf bedrani on Unsplash |
36
+ | `https://images.unsplash.com/photo-1570290887796-27624be22a60?w=1080&q=80&fm=jpg` | Waterfall pouring over dark basalt cliffs | Photo by Ben Eaton on Unsplash |
37
+ | `https://images.unsplash.com/photo-1685466695371-9fa8510634d0?w=1080&q=80&fm=jpg` | Pink poppies in bloom under a clear blue sky | Photo by Steve DiMatteo on Unsplash |
38
+ | `https://images.unsplash.com/photo-1491838592561-ab572ec2d2cb?w=1080&q=80&fm=jpg` | Sunbeams breaking through a misty forest | Photo by Mel on Unsplash |
39
+ | `https://images.unsplash.com/photo-1612441822281-778c7b2d4f38?w=1080&q=80&fm=jpg` | Golden sunrise over layered mountain ridges | Photo by Simon Lohmann on Unsplash |
40
+ | `https://images.unsplash.com/photo-1543988884-c01cfa7b41c2?w=1080&q=80&fm=jpg` | Turquoise water meeting a white sand beach, from above | Photo by Harry Holder on Unsplash |
41
+ | `https://images.unsplash.com/photo-1504596274545-5c136d4338f2?w=1080&q=80&fm=jpg` | Steep mountains flanking a deep blue fjord | Photo by Rod Long on Unsplash |
42
+ | `https://images.unsplash.com/photo-1726486061288-783c5cad8090?w=1080&q=80&fm=jpg` | Autumn trees around a still lake under grey sky | Photo by Linda Christiansen on Unsplash |
43
+ | `https://images.unsplash.com/photo-1665073526612-a2c3d7faddca?w=1080&q=80&fm=jpg` | Sunlit road curving through tall pine trees | Photo by Photos of Korea on Unsplash |
44
+
45
+ ## Portraits and faces
46
+
47
+ One person, face readable at small sizes. Good for avatars, authors and a testimonial's photo.
48
+
49
+ | src | Shows | Credit |
50
+ |---|---|---|
51
+ | `https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=1080&q=80&fm=jpg` | Laughing woman in a red turtleneck, dark wavy hair | Photo from Unsplash |
52
+ | `https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=1080&q=80&fm=jpg` | Woman with slicked-back hair under blue studio light | Photo from Unsplash |
53
+ | `https://images.unsplash.com/photo-1524504388940-b1c1722653e1?w=1080&q=80&fm=jpg` | Woman with long blonde hair on a grey studio backdrop | Photo from Unsplash |
54
+ | `https://images.unsplash.com/photo-1554151228-14d9def656e4?w=1080&q=80&fm=jpg` | Freckled young woman with a braided bun, hand on chin | Photo from Unsplash |
55
+ | `https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=1080&q=80&fm=jpg` | Young woman with a red bob by a lake | Photo from Unsplash |
56
+ | `https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=1080&q=80&fm=jpg` | Man in a grey sweater, studio headshot | Photo from Unsplash |
57
+ | `https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?w=1080&q=80&fm=jpg` | Bearded man in a grey knit sweater on black | Photo from Unsplash |
58
+ | `https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=1080&q=80&fm=jpg` | Smiling bearded man in a white t-shirt | Photo from Unsplash |
59
+ | `https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=1080&q=80&fm=jpg` | Man with grey hair and glasses in a green polo | Photo from Unsplash |
60
+ | `https://images.unsplash.com/photo-1560250097-0b93528c311a?w=1080&q=80&fm=jpg` | Man in glasses and a navy blazer by a window | Photo from Unsplash |
61
+
62
+ ## Groups and people at work
63
+
64
+ More than one person: teams, friends, hands. For about sections, culture pages and testimonials.
65
+
66
+ | src | Shows | Credit |
67
+ |---|---|---|
68
+ | `https://images.unsplash.com/photo-1522071820081-009f0129c71c?w=1080&q=80&fm=jpg` | Team working on laptops at a long wooden table | Photo from Unsplash |
69
+ | `https://images.unsplash.com/photo-1521737604893-d14cc237f11d?w=1080&q=80&fm=jpg` | Group with headphones collaborating around a table | Photo from Unsplash |
70
+ | `https://images.unsplash.com/photo-1543269865-cbf427effbad?w=1080&q=80&fm=jpg` | Four young people talking around a cafe table | Photo from Unsplash |
71
+ | `https://images.unsplash.com/photo-1522202176988-66273c2fd55f?w=1080&q=80&fm=jpg` | Three young women laughing over laptops | Photo from Unsplash |
72
+ | `https://images.unsplash.com/photo-1495474472287-4d71bcdd2085?w=1080&q=80&fm=jpg` | Four hands toasting with coffee cups | Photo from Unsplash |
73
+
74
+ ## Interiors and workspaces
75
+
76
+ Rooms and desks without a face: offices, a restaurant, a working setup.
77
+
78
+ | src | Shows | Credit |
79
+ |---|---|---|
80
+ | `https://images.unsplash.com/photo-1497366216548-37526070297c?w=1080&q=80&fm=jpg` | Modern empty office corridor with glass partitions | Photo from Unsplash |
81
+ | `https://images.unsplash.com/photo-1517248135467-4c7edcad34c4?w=1080&q=80&fm=jpg` | Restaurant interior with dark wood tables and pendant lights | Photo from Unsplash |
82
+ | `https://images.unsplash.com/photo-1498050108023-c5249f4df085?w=1080&q=80&fm=jpg` | Programmer desk with an open laptop, code and coffee | Photo from Unsplash |
83
+ | `https://images.unsplash.com/photo-1517336714731-489689fd1ca8?w=1080&q=80&fm=jpg` | Half-open laptop lit with purple and orange neon | Photo from Unsplash |
84
+ | `https://images.unsplash.com/photo-1504384308090-c894fdcc538d?w=1080&q=80&fm=jpg` | Busy open-plan office with rows of people at laptops | Photo from Unsplash |
85
+
86
+ ## Objects and food
87
+
88
+ A single product or plate on a quiet background: good for cards, features and lists.
89
+
90
+ | src | Shows | Credit |
91
+ |---|---|---|
92
+ | `https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=1080&q=80&fm=jpg` | Black over-ear headphones on a bright yellow background | Photo from Unsplash |
93
+ | `https://images.unsplash.com/photo-1526170375885-4d8ecf77b99f?w=1080&q=80&fm=jpg` | White instant camera on a plain white surface | Photo from Unsplash |
94
+ | `https://images.unsplash.com/photo-1542291026-7eec264c27ff?w=1080&q=80&fm=jpg` | Red running shoe on a matching red background | Photo from Unsplash |
95
+ | `https://images.unsplash.com/photo-1523275335684-37898b6baf30?w=1080&q=80&fm=jpg` | Two smartwatches with blank round faces on white | Photo from Unsplash |
96
+ | `https://images.unsplash.com/photo-1512621776951-a57141f2eefd?w=1080&q=80&fm=jpg` | Colourful salad bowl with avocado, tomatoes and chickpeas | Photo from Unsplash |
97
+ | `https://images.unsplash.com/photo-1567620905732-2d1ec7ab7445?w=1080&q=80&fm=jpg` | Pancake stack with syrup poured from a small cup | Photo from Unsplash |
98
+
99
+ ## Sport, hobbies and daily life
100
+
101
+ Exercise, shopping, music, study and a cafe: for fitness, retail, education and
102
+ lifestyle pages.
103
+
104
+ | src | Shows | Credit |
105
+ |---|---|---|
106
+ | `https://images.unsplash.com/photo-1517836357463-d25dfeac3438?w=1080&q=80&fm=jpg` | Man deadlifting a barbell in a dark gym | Photo from Unsplash |
107
+ | `https://images.unsplash.com/photo-1571019613454-1cb2f99b2d8b?w=1080&q=80&fm=jpg` | Woman doing sit-ups on a mat by a bright window | Photo from Unsplash |
108
+ | `https://images.unsplash.com/photo-1476480862126-209bfaa8edc8?w=1080&q=80&fm=jpg` | Runner's legs climbing weathered concrete steps | Photo from Unsplash |
109
+ | `https://images.unsplash.com/photo-1483985988355-763728e1935b?w=1080&q=80&fm=jpg` | Woman in sunglasses carrying shopping bags outside | Photo from Unsplash |
110
+ | `https://images.unsplash.com/photo-1445205170230-053b83016050?w=1080&q=80&fm=jpg` | Clothing racks full of coats in a boutique | Photo from Unsplash |
111
+ | `https://images.unsplash.com/photo-1554118811-1e0d58224f24?w=1080&q=80&fm=jpg` | Customers and a barista inside a plant-filled cafe | Photo from Unsplash |
112
+ | `https://images.unsplash.com/photo-1511671782779-c97d3d27a1d4?w=1080&q=80&fm=jpg` | Vintage silver microphone against coloured blurred lights | Photo from Unsplash |
113
+ | `https://images.unsplash.com/photo-1503676260728-1c00da094a0b?w=1080&q=80&fm=jpg` | Stack of books, a red apple and alphabet blocks | Photo from Unsplash |
114
+ | `https://images.unsplash.com/photo-1416879595882-3373a0480b5b?w=1080&q=80&fm=jpg` | Gardening flat lay with a soil scoop and spilled potting mix | Photo from Unsplash |
115
+
116
+ ## Cities and buildings
117
+
118
+ Streets, skylines and a house at dusk: for travel, real estate and city pages.
119
+
120
+ | src | Shows | Credit |
121
+ |---|---|---|
122
+ | `https://images.unsplash.com/photo-1449824913935-59a10b8d2000?w=1080&q=80&fm=jpg` | New York avenue with skyscrapers and yellow taxis | Photo from Unsplash |
123
+ | `https://images.unsplash.com/photo-1477959858617-67f85cf4f1df?w=1080&q=80&fm=jpg` | Aerial Chicago skyline at dusk beside the lake | Photo from Unsplash |
124
+ | `https://images.unsplash.com/photo-1519501025264-65ba15a82390?w=1080&q=80&fm=jpg` | Wet New York street with cars at dusk between towers | Photo from Unsplash |
125
+ | `https://images.unsplash.com/photo-1494526585095-c41746248156?w=1080&q=80&fm=jpg` | Modern house exterior at dusk with a warm lit entrance | Photo from Unsplash |
126
+
127
+ ## Textures and backgrounds
128
+
129
+ No subject to recognise: gradients, ink and water for a back layer behind text.
130
+
131
+ | src | Shows | Credit |
132
+ |---|---|---|
133
+ | `https://images.unsplash.com/photo-1557682250-33bd709cbe85?w=1080&q=80&fm=jpg` | Abstract purple to pink gradient | Photo from Unsplash |
134
+ | `https://images.unsplash.com/photo-1557682224-5b8590cd9ec5?w=1080&q=80&fm=jpg` | Abstract blue to orange-red gradient | Photo from Unsplash |
135
+ | `https://images.unsplash.com/photo-1541701494587-cb58502866ab?w=1080&q=80&fm=jpg` | Blue and orange ink swirling in dark water | Photo from Unsplash |
136
+ | `https://images.unsplash.com/photo-1674005619701-285543ad3a40?w=1080&q=80&fm=jpg` | Calm blue water surface with soft ripples | Photo by Ch Photography on Unsplash |
137
+
138
+ ## Animals
139
+
140
+ For pets, farming and anything that wants a friendly non-human.
141
+
142
+ | src | Shows | Credit |
143
+ |---|---|---|
144
+ | `https://images.unsplash.com/photo-1750363890360-2fc2f5136187?w=1080&q=80&fm=jpg` | Brown cow grazing in a lush green meadow | Photo by Seval Torun on Unsplash |
145
+ | `https://images.unsplash.com/photo-1514888286974-6c03e2ca1dba?w=1080&q=80&fm=jpg` | Black and white cat resting on a wooden railing | Photo from Unsplash |
146
+ | `https://images.unsplash.com/photo-1543466835-00a7907e9de1?w=1080&q=80&fm=jpg` | Beagle looking up with its tongue out | Photo from Unsplash |
147
+
148
+ ## Licence
149
+
150
+ The photos come from [Unsplash](https://unsplash.com) under the
151
+ [Unsplash License](https://unsplash.com/license): free to download, copy, modify and
152
+ use, including commercially, without permission and without attribution. Three things
153
+ that licence does not cover, and that matter to a design:
154
+
155
+ - **Reselling or compiling.** You cannot sell unmodified copies, and you cannot build a
156
+ competing photo service out of them. A design, a mockup and a shipped page are fine.
157
+ - **Unsplash+.** Its paid photos are a different licence. None are in this file.
158
+ - **People and trademarks.** The faces above belong to real people with rights of
159
+ publicity; the object photos may show a brand. A stock face in an avatar or a mockup
160
+ is ordinary use. A shipped page that presents one as a real customer's testimonial,
161
+ or a logo as your product, is not covered by the photo licence.
162
+
163
+ The images are **hotlinked**, never copied into the repository: the design file stays
164
+ text, and the photos render only while there is a connection. A photo its author
165
+ deletes stops resolving, so run `npm run images:check` before a release that depends
166
+ on them; it prints every URL that no longer resolves.
167
+
168
+ A credit reads `Photo by <name> on Unsplash` when the photographer's name was recorded
169
+ (the photos inherited from `examples/miniprogram.pen`), and `Photo from Unsplash` when
170
+ it was not. The licence does not require attribution, so either one is a complete
171
+ credit; the first is simply easier to trace.