@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.
- package/README.md +7 -6
- package/manifest.json +17 -68
- package/package.json +6 -1
- package/scripts/design-upstream.js +92 -0
- package/scripts/doctor.js +0 -24
- package/scripts/lib/default-skill-floor.js +5 -1
- package/scripts/lib/domains.js +2 -2
- package/scripts/lib/recommend.js +1 -3
- package/scripts/lib/retired-skills.js +7 -1
- package/skills/design/IMPECCABLE-LICENSE.txt +191 -0
- package/skills/design/SKILL.md +79 -328
- package/skills/design/THIRD_PARTY.md +26 -0
- package/skills/design/engine/LICENSE +21 -0
- package/skills/design/engine/RUN.md +515 -0
- package/skills/design/engine/UPSTREAM.json +4 -0
- package/skills/design/engine/assets/app.css +216 -0
- package/skills/design/engine/assets/base.css +105 -0
- package/skills/design/engine/assets/base.js +37 -0
- package/skills/design/engine/assets/charts.js +617 -0
- package/skills/design/engine/assets/scenes/stage.css +28 -0
- package/skills/design/engine/assets/scenes/stage.js +133 -0
- package/skills/design/engine/library/app.json +1327 -0
- package/skills/design/engine/library/directions.json +1078 -0
- package/skills/design/engine/library/heroes.json +1086 -0
- package/skills/design/engine/library/motions.json +92 -0
- package/skills/design/engine/library/palettes.json +1485 -0
- package/skills/design/engine/library/photos.json +7240 -0
- package/skills/design/engine/library/sections.json +4429 -0
- package/skills/design/engine/library/sites.json +8350 -0
- package/skills/design/engine/library/type.json +2024 -0
- package/skills/design/engine/references/app-screens.md +1199 -0
- package/skills/design/engine/references/colour.md +141 -0
- package/skills/design/engine/references/copy.md +177 -0
- package/skills/design/engine/references/critique.md +182 -0
- package/skills/design/engine/references/imagery.md +791 -0
- package/skills/design/engine/references/layout.md +292 -0
- package/skills/design/engine/references/motion.md +980 -0
- package/skills/design/engine/references/research.md +356 -0
- package/skills/design/engine/references/scenes.md +174 -0
- package/skills/design/engine/references/tells.md +115 -0
- package/skills/design/engine/references/type.md +170 -0
- package/skills/design/engine/scripts/assets.mjs +865 -0
- package/skills/design/engine/scripts/draw.mjs +584 -0
- package/skills/design/engine/scripts/inspect.mjs +303 -0
- package/skills/design/engine/scripts/lib/app-draw.mjs +203 -0
- package/skills/design/engine/scripts/lib/cdp.mjs +248 -0
- package/skills/design/engine/scripts/lib/probe.mjs +909 -0
- package/skills/design/engine/scripts/lib/rules.mjs +243 -0
- package/skills/design/engine/scripts/lint.mjs +119 -0
- package/skills/design/engine/scripts/scenes.mjs +124 -0
- package/skills/design/evals/cases.yaml +35 -46
- package/skills/design/references/design-md.md +49 -0
- package/skills/design/references/finish-review.md +46 -0
- package/skills/design/references/modes.md +51 -0
- package/skills/expo/SKILL.md +3 -4
- package/skills/marketing/references/landing-copy.md +2 -2
- package/skills/react/SKILL.md +2 -2
- package/skills/react-native/SKILL.md +3 -3
- package/skills/swift-ios/SKILL.md +1 -1
- package/skills/ui-engineering/SKILL.md +3 -4
- package/skills/ui-engineering/evals/cases.yaml +2 -2
- package/skills/variant-explorer/SKILL.md +4 -4
- package/skills/variant-explorer/evals/cases.yaml +1 -1
- package/targets/clone-bootstrap.mjs +1 -1
- package/scripts/lib/design-identity.js +0 -117
- package/scripts/lib/starting-point.js +0 -314
- package/skills/design/evals/README.md +0 -53
- package/skills/design/references/ai-tells.md +0 -75
- package/skills/design/references/brand-grounding.md +0 -197
- package/skills/design/references/copywriting-frameworks.md +0 -138
- package/skills/design/references/design-systems.md +0 -49
- package/skills/design/references/inspiration-sources.md +0 -96
- package/skills/design/references/landing-anatomy-and-cro.md +0 -202
- package/skills/design/references/motion-and-interaction.md +0 -232
- package/skills/design/references/research-method.md +0 -154
- package/skills/design/references/signature-and-craft.md +0 -148
- package/skills/design/references/starting-point.md +0 -75
- package/skills/design/references/trends-2026.md +0 -80
- package/skills/design/references/visual-system.md +0 -237
- package/skills/design/scripts/verify.sh +0 -357
- package/skills/design-dna/PROMPT.md +0 -211
- package/skills/design-dna/SKILL.md +0 -162
- package/skills/design-dna/design-dna.schema.json +0 -230
- package/skills/design-dna/evals/README.md +0 -64
- package/skills/design-dna/evals/cases.yaml +0 -68
- package/skills/design-dna/references/method.md +0 -436
- package/skills/design-dna/scripts/emit.py +0 -164
- package/skills/design-dna/templates/PROMPT.md.tmpl +0 -42
- package/skills/design-dna/templates/SKILL.md.tmpl +0 -42
- package/skills/design-dna/templates/check.py.tmpl +0 -143
- package/skills/design-loop/SKILL.md +0 -160
- package/skills/design-loop/evals/README.md +0 -56
- package/skills/design-loop/evals/cases.yaml +0 -60
- package/skills/motion-craft/SKILL.md +0 -125
- package/skills/motion-craft/evals/cases.yaml +0 -38
- 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
|
+
})();
|