@ericrisco/rsc 3.1.4 → 3.3.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 (96) hide show
  1. package/README.md +7 -6
  2. package/manifest.json +17 -68
  3. package/package.json +6 -1
  4. package/scripts/design-upstream.js +92 -0
  5. package/scripts/doctor.js +0 -24
  6. package/scripts/lib/default-skill-floor.js +5 -1
  7. package/scripts/lib/domains.js +2 -2
  8. package/scripts/lib/recommend.js +1 -3
  9. package/scripts/lib/retired-skills.js +7 -1
  10. package/skills/design/IMPECCABLE-LICENSE.txt +191 -0
  11. package/skills/design/SKILL.md +79 -328
  12. package/skills/design/THIRD_PARTY.md +26 -0
  13. package/skills/design/engine/LICENSE +21 -0
  14. package/skills/design/engine/RUN.md +515 -0
  15. package/skills/design/engine/UPSTREAM.json +4 -0
  16. package/skills/design/engine/assets/app.css +216 -0
  17. package/skills/design/engine/assets/base.css +105 -0
  18. package/skills/design/engine/assets/base.js +37 -0
  19. package/skills/design/engine/assets/charts.js +617 -0
  20. package/skills/design/engine/assets/scenes/stage.css +28 -0
  21. package/skills/design/engine/assets/scenes/stage.js +133 -0
  22. package/skills/design/engine/library/app.json +1327 -0
  23. package/skills/design/engine/library/directions.json +1078 -0
  24. package/skills/design/engine/library/heroes.json +1086 -0
  25. package/skills/design/engine/library/motions.json +92 -0
  26. package/skills/design/engine/library/palettes.json +1485 -0
  27. package/skills/design/engine/library/photos.json +7240 -0
  28. package/skills/design/engine/library/sections.json +4429 -0
  29. package/skills/design/engine/library/sites.json +8350 -0
  30. package/skills/design/engine/library/type.json +2024 -0
  31. package/skills/design/engine/references/app-screens.md +1199 -0
  32. package/skills/design/engine/references/colour.md +141 -0
  33. package/skills/design/engine/references/copy.md +177 -0
  34. package/skills/design/engine/references/critique.md +182 -0
  35. package/skills/design/engine/references/imagery.md +791 -0
  36. package/skills/design/engine/references/layout.md +292 -0
  37. package/skills/design/engine/references/motion.md +980 -0
  38. package/skills/design/engine/references/research.md +356 -0
  39. package/skills/design/engine/references/scenes.md +174 -0
  40. package/skills/design/engine/references/tells.md +115 -0
  41. package/skills/design/engine/references/type.md +170 -0
  42. package/skills/design/engine/scripts/assets.mjs +865 -0
  43. package/skills/design/engine/scripts/draw.mjs +584 -0
  44. package/skills/design/engine/scripts/inspect.mjs +303 -0
  45. package/skills/design/engine/scripts/lib/app-draw.mjs +203 -0
  46. package/skills/design/engine/scripts/lib/cdp.mjs +248 -0
  47. package/skills/design/engine/scripts/lib/probe.mjs +909 -0
  48. package/skills/design/engine/scripts/lib/rules.mjs +243 -0
  49. package/skills/design/engine/scripts/lint.mjs +119 -0
  50. package/skills/design/engine/scripts/scenes.mjs +124 -0
  51. package/skills/design/evals/cases.yaml +35 -46
  52. package/skills/design/references/design-md.md +49 -0
  53. package/skills/design/references/finish-review.md +46 -0
  54. package/skills/design/references/modes.md +51 -0
  55. package/skills/expo/SKILL.md +3 -4
  56. package/skills/marketing/references/landing-copy.md +2 -2
  57. package/skills/react/SKILL.md +2 -2
  58. package/skills/react-native/SKILL.md +3 -3
  59. package/skills/swift-ios/SKILL.md +1 -1
  60. package/skills/ui-engineering/SKILL.md +3 -4
  61. package/skills/ui-engineering/evals/cases.yaml +2 -2
  62. package/skills/variant-explorer/SKILL.md +4 -4
  63. package/skills/variant-explorer/evals/cases.yaml +1 -1
  64. package/targets/clone-bootstrap.mjs +1 -1
  65. package/scripts/lib/design-identity.js +0 -117
  66. package/scripts/lib/starting-point.js +0 -314
  67. package/skills/design/evals/README.md +0 -53
  68. package/skills/design/references/ai-tells.md +0 -75
  69. package/skills/design/references/brand-grounding.md +0 -197
  70. package/skills/design/references/copywriting-frameworks.md +0 -138
  71. package/skills/design/references/design-systems.md +0 -49
  72. package/skills/design/references/inspiration-sources.md +0 -96
  73. package/skills/design/references/landing-anatomy-and-cro.md +0 -202
  74. package/skills/design/references/motion-and-interaction.md +0 -232
  75. package/skills/design/references/research-method.md +0 -154
  76. package/skills/design/references/signature-and-craft.md +0 -148
  77. package/skills/design/references/starting-point.md +0 -75
  78. package/skills/design/references/trends-2026.md +0 -80
  79. package/skills/design/references/visual-system.md +0 -237
  80. package/skills/design/scripts/verify.sh +0 -357
  81. package/skills/design-dna/PROMPT.md +0 -211
  82. package/skills/design-dna/SKILL.md +0 -162
  83. package/skills/design-dna/design-dna.schema.json +0 -230
  84. package/skills/design-dna/evals/README.md +0 -64
  85. package/skills/design-dna/evals/cases.yaml +0 -68
  86. package/skills/design-dna/references/method.md +0 -436
  87. package/skills/design-dna/scripts/emit.py +0 -164
  88. package/skills/design-dna/templates/PROMPT.md.tmpl +0 -42
  89. package/skills/design-dna/templates/SKILL.md.tmpl +0 -42
  90. package/skills/design-dna/templates/check.py.tmpl +0 -143
  91. package/skills/design-loop/SKILL.md +0 -160
  92. package/skills/design-loop/evals/README.md +0 -56
  93. package/skills/design-loop/evals/cases.yaml +0 -60
  94. package/skills/motion-craft/SKILL.md +0 -125
  95. package/skills/motion-craft/evals/cases.yaml +0 -38
  96. package/skills/motion-craft/scripts/verify.sh +0 -75
@@ -0,0 +1,28 @@
1
+ /* tasteskill / scenes / stage.css — the held stage under every scroll scene (references/scenes.md).
2
+ Without .is-live (no script, a failed CDN, reduced motion, a phone set to 'rest') a scene is an ordinary
3
+ section showing its finished composition. With it, the section is (hold + 1) screens tall and the
4
+ stage sticks to the viewport for the hold. */
5
+
6
+ .scene { position: relative; }
7
+ .scene > .scene-stage { position: relative; }
8
+
9
+ html.js .scene.is-live { height: calc((var(--hold, 2) + 1) * 100svh); }
10
+ html.js .scene.is-live > .scene-stage {
11
+ position: sticky;
12
+ top: 0;
13
+ height: 100svh;
14
+ overflow: clip; /* clip, not hidden: hidden would make the stage a scroll container */
15
+ contain: layout paint;
16
+ }
17
+
18
+ /* Everything a scene moves is composited; nothing it moves is laid out again. */
19
+ .scene.is-live [data-move] { will-change: transform, opacity; }
20
+
21
+ /* Lenis */
22
+ html.is-smooth { scroll-behavior: auto; }
23
+ html.is-smooth body { overscroll-behavior: none; }
24
+
25
+ @media (prefers-reduced-motion: reduce) {
26
+ html.js .scene.is-live { height: auto; }
27
+ html.js .scene.is-live > .scene-stage { position: relative; height: auto; }
28
+ }
@@ -0,0 +1,133 @@
1
+ // tasteskill / scenes / stage.js — the shared stage every scroll scene runs on (references/scenes.md).
2
+ //
3
+ // A scene is a tall section whose stage sticks to the viewport while the page scrolls past it; a GSAP
4
+ // timeline is scrubbed by that scroll. The page always scrolls natively (no wheel capture), the
5
+ // scrollbar always tells the truth, and the finished composition is what reduced motion, no script and
6
+ // a failed CDN all show.
7
+ //
8
+ // Load at the end of <body>, in this order (versions pinned):
9
+ // <script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
10
+ // <script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/ScrollTrigger.min.js"></script>
11
+ // <script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/SplitText.min.js"></script> (only if a scene splits text)
12
+ // <script src="https://cdn.jsdelivr.net/npm/lenis@1.3.26/dist/lenis.min.js"></script> (optional smoothing)
13
+ // <script src="assets/scenes/stage.js"></script>
14
+ // <script src="assets/scenes/<scene>.js"></script>
15
+ // and keep the one-line <script>document.documentElement.classList.add('js')</script> in <head>.
16
+ //
17
+ // Stage.smooth() weighted wheel scrolling (Lenis), off for touch and reduced motion
18
+ // Stage.scene(root, { hold, scrub, build, phone, rest })
19
+ // Stage.split(el, 'lines' | 'words' | 'chars') masked split text, SplitText when loaded
20
+ // Stage.ready() resolves when fonts and the scenes' pictures are ready
21
+ // Stage.init() starts every <section class="scene" data-scene="<id>"> on the page
22
+ //
23
+ // A page: Stage.smooth(); Stage.init();
24
+ (() => {
25
+ const html = document.documentElement;
26
+ const mq = (q) => window.matchMedia(q).matches;
27
+ const live = !!(window.gsap && window.ScrollTrigger);
28
+ if (live) gsap.registerPlugin(ScrollTrigger);
29
+ if (live && window.SplitText) gsap.registerPlugin(SplitText);
30
+
31
+ const Stage = {
32
+ live,
33
+ get reduced() { return mq('(prefers-reduced-motion: reduce)'); },
34
+ get phone() { return mq('(max-width: 767px)'); },
35
+ get touch() { return mq('(pointer: coarse)'); },
36
+ lenis: null,
37
+ scenes: [],
38
+ };
39
+
40
+ // Weighted wheel scrolling. lerp 0.1-0.14 feels heavy without lagging behind the hand; touch keeps the
41
+ // phone's own scrolling, and reduced motion keeps the browser's.
42
+ Stage.smooth = ({ lerp = 0.12 } = {}) => {
43
+ if (!live || !window.Lenis || Stage.reduced || Stage.touch || Stage.lenis) return Stage.lenis;
44
+ const lenis = new Lenis({ lerp, smoothWheel: true, anchors: true, autoRaf: false });
45
+ lenis.on('scroll', ScrollTrigger.update);
46
+ gsap.ticker.add((t) => lenis.raf(t * 1000));
47
+ gsap.ticker.lagSmoothing(0);
48
+ html.classList.add('is-smooth');
49
+ Stage.lenis = lenis;
50
+ return lenis;
51
+ };
52
+
53
+ // One scroll scene.
54
+ // root <section class="scene"> holding one <div class="scene-stage">
55
+ // hold screens of scroll the stage is held for (1-4); the section is (hold + 1) screens tall
56
+ // scrub true = locked to the scrollbar; a number = seconds of catch-up (0.3-0.8 reads as weight)
57
+ // build(tl, ctx) add the tweens to tl; the timeline runs 0 → 1 over the hold. ctx: { root, stage, phone, q }
58
+ // phone 'same' (default) | 'rest' (the phone gets the finished composition) | { hold, build }
59
+ // rest(ctx) optional: anything extra the finished composition needs (most scenes need nothing)
60
+ // Sets --p (0-1) and data-beat on root, so CSS and captions can follow the scene too.
61
+ Stage.scene = (root, opts = {}) => {
62
+ if (!root) return null;
63
+ const { hold = 2, scrub = true, build, phone = 'same', rest, beats = 0 } = opts;
64
+ const stage = root.querySelector(':scope > .scene-stage') || root.firstElementChild;
65
+ const q = (sel) => [...root.querySelectorAll(sel)];
66
+ const toRest = (ctx) => { root.classList.remove('is-live'); root.classList.add('is-rest'); root.style.removeProperty('--hold'); if (rest) rest(ctx); };
67
+ if (!live) { toRest({ root, stage, phone: Stage.phone, q }); return null; }
68
+ const mm = gsap.matchMedia();
69
+ mm.add({ motion: '(prefers-reduced-motion: no-preference)', phone: '(max-width: 767px)' }, (c) => {
70
+ const isPhone = c.conditions.phone;
71
+ const ctx = { root, stage, phone: isPhone, q };
72
+ const plan = isPhone ? (phone === 'rest' ? null : phone === 'same' ? { hold, build } : { hold: phone.hold ?? hold, build: phone.build ?? build }) : { hold, build };
73
+ if (!c.conditions.motion || !plan || !plan.build) { toRest(ctx); return () => root.classList.remove('is-rest'); }
74
+ root.classList.remove('is-rest');
75
+ root.classList.add('is-live');
76
+ root.style.setProperty('--hold', plan.hold);
77
+ const tl = gsap.timeline({
78
+ defaults: { ease: 'none' },
79
+ scrollTrigger: {
80
+ trigger: root, start: 'top top', end: 'bottom bottom', scrub, invalidateOnRefresh: true,
81
+ onUpdate: (self) => {
82
+ root.style.setProperty('--p', self.progress.toFixed(4));
83
+ if (beats) root.dataset.beat = String(Math.min(beats - 1, Math.floor(self.progress * beats)));
84
+ },
85
+ },
86
+ });
87
+ plan.build(tl, ctx);
88
+ // pad the timeline to exactly 1 so positions in build() read as fractions of the hold
89
+ if (tl.duration() < 1) tl.to({}, { duration: 1 - tl.duration() }, tl.duration());
90
+ return () => { root.classList.remove('is-live'); root.style.removeProperty('--hold'); root.style.removeProperty('--p'); };
91
+ });
92
+ Stage.scenes.push(mm);
93
+ return mm;
94
+ };
95
+
96
+ // Masked split text. Lines rise out of their own mask; words and characters are for short headings.
97
+ Stage.split = (el, type = 'lines') => {
98
+ if (!el) return [];
99
+ if (window.SplitText && live) {
100
+ const s = SplitText.create(el, { type: type === 'lines' ? 'lines' : type === 'words' ? 'lines,words' : 'lines,words,chars', mask: 'lines', aria: 'auto', autoSplit: false });
101
+ return type === 'lines' ? s.lines : type === 'words' ? s.words : s.chars;
102
+ }
103
+ // fallback: words in inline-block spans, the full text kept for screen readers
104
+ if (!el.dataset.split) {
105
+ el.setAttribute('aria-label', el.textContent.trim());
106
+ el.innerHTML = el.textContent.trim().split(/\s+/).map((w) => `<span aria-hidden="true" style="display:inline-block">${w}</span>`).join(' ');
107
+ el.dataset.split = 'words';
108
+ }
109
+ return [...el.children];
110
+ };
111
+
112
+ // Fonts and the scenes' pictures decide every height a trigger measures; refresh once they are in.
113
+ Stage.ready = () => {
114
+ const imgs = [...document.querySelectorAll('.scene img')].map((i) => (i.complete ? null : i.decode().catch(() => {}))).filter(Boolean);
115
+ return Promise.all([document.fonts ? document.fonts.ready : null, ...imgs]).then(() => { if (live) ScrollTrigger.refresh(); });
116
+ };
117
+
118
+ // Every <section class="scene" data-scene="<id>" data-options='{"hold":3}'> on the page, started by
119
+ // the scene file of that id (assets/scenes/<id>.js registers Scenes['<id>']).
120
+ Stage.init = () => {
121
+ for (const el of document.querySelectorAll('.scene[data-scene]')) {
122
+ if (el.__scene) continue;
123
+ const make = window.Scenes[el.dataset.scene];
124
+ if (!make) { console.warn('No scene file loaded for data-scene="' + el.dataset.scene + '"'); continue; }
125
+ let o = {}; try { o = JSON.parse(el.dataset.options || '{}'); } catch {}
126
+ el.__scene = make(el, o) || true;
127
+ }
128
+ };
129
+
130
+ window.Scenes = window.Scenes || {};
131
+ window.Stage = Stage;
132
+ if (live) { window.addEventListener('load', () => Stage.ready()); }
133
+ })();