@linear_non/stellar-kit 3.0.1 → 3.0.2

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.
@@ -110,13 +110,15 @@ export class ApplicationManager {
110
110
 
111
111
  /**
112
112
  * Initialize and activate all global systems.
113
- * @param {{ isSmooth: boolean }} options
113
+ * @param {{ isSmooth: boolean, useExternalScroll?: boolean }} options
114
114
  * @private
115
115
  */
116
- _initSystems({ isSmooth }) {
116
+ _initSystems({ isSmooth, useExternalScroll = false }) {
117
117
  const resize = new Resize()
118
118
  const mouse = new Mouse()
119
- const scroll = new Scroll({ isSmooth: isSmooth })
119
+
120
+ // If using external scroll (Lenis, etc.), do NOT create Scroll.
121
+ const scroll = useExternalScroll ? null : new Scroll({ isSmooth })
120
122
  const raf = new Raf()
121
123
 
122
124
  this.systems = { resize, mouse, scroll, raf }
@@ -127,6 +129,11 @@ export class ApplicationManager {
127
129
  kitStore.raf = raf
128
130
 
129
131
  kitStore.flags.isSmooth = isSmooth
132
+ kitStore.flags.useExternalScroll = useExternalScroll
133
+
134
+ if (useExternalScroll) {
135
+ document.body.classList.add("is-external-scroll")
136
+ }
130
137
 
131
138
  // Phase 1: init
132
139
  Object.values(this.systems).forEach(sys => {
@@ -165,6 +172,10 @@ export class ApplicationManager {
165
172
  Object.values(this.systems).forEach(sys => {
166
173
  if (sys && typeof sys.destroy === "function") sys.destroy()
167
174
  })
175
+
176
+ kitStore.scroll = null
177
+ kitStore.flags.useExternalScroll = false
178
+
168
179
  this.initialized = false
169
180
  }
170
181
 
@@ -177,23 +188,40 @@ export class ApplicationManager {
177
188
  * page: { element?: string, container?: string },
178
189
  * load?: string,
179
190
  * isSmooth?: boolean,
191
+ * useExternalScroll?: boolean,
180
192
  * dim?: { d?: number, m?: number }
181
193
  * }} config
182
194
  * @returns {Promise<typeof kitStore>}
183
195
  */
184
196
  async initialize(config) {
185
197
  if (this.initialized) return kitStore
186
- if (!sniffer.isDesktop) {
187
- config.isSmooth = false
188
- } else if (!config.isSmooth && sniffer.isDesktop) {
189
- config.isSmooth = true
198
+
199
+ const useExternalScroll = Boolean(config.useExternalScroll)
200
+
201
+ // Only apply the desktop heuristic for internal Scroll mode
202
+ if (!useExternalScroll) {
203
+ if (!sniffer.isDesktop) {
204
+ config.isSmooth = false
205
+ } else if (!config.isSmooth && sniffer.isDesktop) {
206
+ config.isSmooth = true
207
+ }
208
+ } else {
209
+ // External scroll engine usually behaves like smooth scroll
210
+ if (typeof config.isSmooth === "undefined") {
211
+ config.isSmooth = true
212
+ }
190
213
  }
191
214
 
192
215
  await this._waitDOM()
193
- Debug.log("APP", "DOM ready with", config.isSmooth ? "smooth " : "native " + "scroll.")
216
+ Debug.log(
217
+ "APP",
218
+ "DOM ready with",
219
+ config.isSmooth ? "smooth scroll." : "native scroll.",
220
+ useExternalScroll ? "-- external engine" : "-- internal"
221
+ )
194
222
 
195
223
  this._initStore(config)
196
- this._initSystems({ isSmooth: config.isSmooth })
224
+ this._initSystems({ isSmooth: config.isSmooth, useExternalScroll })
197
225
 
198
226
  this.initialized = true
199
227
  Debug.log("APP", "Application ready.", kitStore)
@@ -260,8 +288,6 @@ export class ApplicationManager {
260
288
  if (this.systems.raf?.setScrollTrigger) {
261
289
  this.systems.raf.setScrollTrigger()
262
290
  }
263
-
264
- this._softResize()
265
291
  }
266
292
 
267
293
  /**
@@ -275,18 +301,6 @@ export class ApplicationManager {
275
301
  document.documentElement.style.setProperty("--mobile", m)
276
302
  }
277
303
 
278
- /**
279
- * Trigger a "soft resize":
280
- * - Emit resize event
281
- * - Refresh ScrollTrigger
282
- * @private
283
- */
284
- _softResize() {
285
- Debug.log("APP", "Soft resize triggered.")
286
- emitter.emit(EVENTS.APP_SMOOTH_RESIZE)
287
- ScrollTrigger.refresh()
288
- }
289
-
290
304
  /**
291
305
  * Wait for DOM ready before execution.
292
306
  * @returns {Promise<void>}
@@ -133,19 +133,38 @@ export default class PageEngine {
133
133
  }
134
134
 
135
135
  /**
136
- * Receives scroll and mouse data from Raf (APP_TICK).
137
- * Packages them and forwards to Manager.tick().
136
+ * Receives data from global Raf (APP_TICK).
137
+ * In internal mode, uses {current, diff} from Scroll.js.
138
+ * In external mode (Lenis), calls smooth.raf() and derives scroll.
138
139
  *
139
140
  * @param {{ current: number, diff: number, mouse: EngineMouseState }} param0
140
141
  */
141
142
  tick = ({ current, diff, mouse }) => {
142
- const { isResizing, isLocked } = kitStore.flags
143
+ const { isResizing, isLocked, useExternalScroll } = kitStore.flags
143
144
 
144
145
  if (isResizing || isLocked) return
145
146
  if (!this.manager) return
146
147
 
147
- this.scroll.current = current
148
- this.scroll.direction = diff > 0 ? "down" : "up"
148
+ let nextScroll = current
149
+ let delta = diff
150
+
151
+ if (useExternalScroll && this.smooth?.raf) {
152
+ const t = performance.now()
153
+ this.smooth.raf(t)
154
+
155
+ if (typeof this.smooth.scroll === "number") {
156
+ nextScroll = this.smooth.scroll
157
+ } else if (typeof this.smooth.targetScroll === "number") {
158
+ nextScroll = this.smooth.targetScroll
159
+ } else {
160
+ nextScroll = window.scrollY || window.pageYOffset || 0
161
+ }
162
+
163
+ delta = nextScroll - this.scroll.current
164
+ }
165
+
166
+ this.scroll.current = nextScroll
167
+ this.scroll.direction = delta > 0 ? "down" : "up"
149
168
 
150
169
  this.mouse.x = mouse.x
151
170
  this.mouse.y = mouse.y
@@ -163,7 +182,11 @@ export default class PageEngine {
163
182
  resize = () => {
164
183
  Debug.log("ENGINE", "Resize")
165
184
 
185
+ // Internal Smooth
166
186
  this.smooth?.update?.()
187
+ // Lenis
188
+ this.smooth?.resize?.()
189
+
167
190
  this.manager?.resize?.()
168
191
  }
169
192
 
package/events/Raf.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { kitStore } from "../kitStore"
2
2
  import emitter, { EVENTS } from "./Emitter"
3
- import { lerp } from "../utils"
3
+ import { Debug, lerp } from "../utils"
4
4
  import { gsap, ScrollTrigger } from "../libraries/gsap"
5
5
 
6
6
  export default class Raf {
@@ -139,6 +139,12 @@ export default class Raf {
139
139
  const { pageContent } = kitStore
140
140
  const scroller = pageContent || document.body
141
141
 
142
+ Debug.log(
143
+ "RAF",
144
+ "Setting ScrollTrigger scroller to:",
145
+ `#${scroller.id}` || `.${scroller.className}` || scroller.tagName
146
+ )
147
+
142
148
  ScrollTrigger.defaults({
143
149
  scroller,
144
150
  })
package/package.json CHANGED
@@ -1,8 +1,10 @@
1
1
  {
2
2
  "name": "@linear_non/stellar-kit",
3
- "version": "3.0.1",
3
+ "version": "3.0.2",
4
4
  "description": "Stellar frontend core for Non-Linear Studio projects.",
5
+ "type": "module",
5
6
  "main": "/core/index.js",
7
+ "sass": "./styles/index.scss",
6
8
  "exports": {
7
9
  ".": "./kitStore.js",
8
10
  "./utils": "./utils/index.js",
@@ -10,9 +12,10 @@
10
12
  "./classes": "./classes/index.js",
11
13
  "./events": "./events/index.js",
12
14
  "./plugins": "./plugins/index.js",
13
- "./gsap": "./libraries/gsap/index.js"
15
+ "./gsap": "./libraries/gsap/index.js",
16
+ "./styles": "./styles/index.scss",
17
+ "./styles/helpers": "./styles/helpers/index.scss"
14
18
  },
15
- "type": "module",
16
19
  "scripts": {
17
20
  "dev": "vite",
18
21
  "build": "vite build",
@@ -27,6 +30,7 @@
27
30
  "utils/",
28
31
  "libraries/",
29
32
  "libraries/gsap/",
33
+ "styles/",
30
34
  "kitStore.js",
31
35
  "index.js"
32
36
  ],
@@ -0,0 +1,176 @@
1
+ * {
2
+ margin: 0;
3
+ padding: 0;
4
+ text-rendering: optimizeLegibility;
5
+ -webkit-font-smoothing: antialiased;
6
+ -moz-osx-font-smoothing: grayscale;
7
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
8
+ }
9
+
10
+ *,
11
+ *::after,
12
+ *::before {
13
+ box-sizing: border-box;
14
+ }
15
+
16
+ html,
17
+ body,
18
+ div,
19
+ span,
20
+ applet,
21
+ object,
22
+ iframe,
23
+ h1,
24
+ h2,
25
+ h3,
26
+ h4,
27
+ h5,
28
+ h6,
29
+ p,
30
+ blockquote,
31
+ pre,
32
+ a,
33
+ abbr,
34
+ acronym,
35
+ address,
36
+ big,
37
+ cite,
38
+ code,
39
+ del,
40
+ dfn,
41
+ em,
42
+ img,
43
+ ins,
44
+ kbd,
45
+ q,
46
+ s,
47
+ samp,
48
+ small,
49
+ strike,
50
+ strong,
51
+ sub,
52
+ sup,
53
+ tt,
54
+ var,
55
+ b,
56
+ u,
57
+ i,
58
+ center,
59
+ dl,
60
+ dt,
61
+ dd,
62
+ ol,
63
+ ul,
64
+ li,
65
+ fieldset,
66
+ form,
67
+ label,
68
+ legend,
69
+ table,
70
+ caption,
71
+ tbody,
72
+ tfoot,
73
+ thead,
74
+ tr,
75
+ th,
76
+ td,
77
+ article,
78
+ aside,
79
+ canvas,
80
+ details,
81
+ embed,
82
+ figure,
83
+ figcaption,
84
+ footer,
85
+ header,
86
+ hgroup,
87
+ menu,
88
+ nav,
89
+ output,
90
+ ruby,
91
+ section,
92
+ summary,
93
+ time,
94
+ mark,
95
+ audio,
96
+ video {
97
+ border: 0;
98
+ font: inherit;
99
+ font-size: 100%;
100
+ margin: 0;
101
+ padding: 0;
102
+ vertical-align: baseline;
103
+ }
104
+
105
+ article,
106
+ aside,
107
+ details,
108
+ figcaption,
109
+ figure,
110
+ footer,
111
+ header,
112
+ hgroup,
113
+ menu,
114
+ nav,
115
+ section {
116
+ display: block;
117
+ }
118
+
119
+ html {
120
+ -webkit-font-smoothing: antialiased;
121
+ -moz-osx-font-smoothing: grayscale;
122
+ text-rendering: optimizelegibility;
123
+ }
124
+
125
+ .is-external-scroll {
126
+ body,
127
+ html {
128
+ position: fixed;
129
+ top: 0;
130
+ left: 0;
131
+ width: 100%;
132
+ height: 100%;
133
+ }
134
+ }
135
+
136
+ a {
137
+ color: inherit;
138
+ text-decoration: none;
139
+ }
140
+
141
+ button,
142
+ input,
143
+ textarea {
144
+ background: none;
145
+ border: none;
146
+ color: inherit;
147
+ font: inherit;
148
+ padding: 0;
149
+ }
150
+
151
+ button {
152
+ cursor: pointer;
153
+ }
154
+
155
+ ol,
156
+ ul {
157
+ list-style: none;
158
+ }
159
+
160
+ blockquote,
161
+ q {
162
+ quotes: none;
163
+ }
164
+
165
+ blockquote::before,
166
+ blockquote::after,
167
+ q::before,
168
+ q::after {
169
+ content: "";
170
+ content: none;
171
+ }
172
+
173
+ table {
174
+ border-collapse: collapse;
175
+ border-spacing: 0;
176
+ }
@@ -0,0 +1,76 @@
1
+ @use "sass:map";
2
+ @use "sass:math";
3
+
4
+ /*=============================================
5
+ = Viewport Tokens =
6
+ =============================================*/
7
+
8
+ $xs_start: 320px;
9
+ $xs_end: 500px;
10
+ $s_start: 501px;
11
+ $s_end: 749px;
12
+ $m_start: 750px;
13
+ $m_end: 1024px;
14
+ $l_start: 1025px;
15
+ $xl_start: 1280px;
16
+ $xxl_start: 1440px;
17
+ $plus_start: 1600px;
18
+ $plusx_start: 1920px;
19
+ $plusxx_start: 2880px;
20
+
21
+ /*=============================================
22
+ = Breakpoint Aliases =
23
+ =============================================*/
24
+
25
+ $breakpoints: (
26
+ xs: "screen and (max-width: #{$xs_end})",
27
+ s: "screen and (max-width: #{$s_end})",
28
+ "s-up": "screen and (min-width: #{$s_start})",
29
+ m: "screen and (max-width: #{$m_end})",
30
+ "m-up": "screen and (min-width: #{$m_start})",
31
+ "l-up": "screen and (min-width: #{$l_start})",
32
+ "xl-up": "screen and (min-width: #{$xl_start})",
33
+ "xxl-up": "screen and (min-width: #{$xxl_start})",
34
+ "plus-up": "screen and (min-width: #{$plus_start})",
35
+ "plusx-up": "screen and (min-width: #{$plusx_start})",
36
+ "plusxx-up": "screen and (min-width: #{$plusxx_start})",
37
+ landscape: "(orientation: landscape) and (min-aspect-ratio: 1920/1079)",
38
+ );
39
+
40
+ /*=============================================
41
+ = Media Query Mixin =
42
+ =============================================*/
43
+ /**
44
+ * Media query helper.
45
+ *
46
+ * Purpose:
47
+ * Provides a single, central API for responsive rules.
48
+ * Avoids scattering raw media queries throughout the codebase.
49
+ *
50
+ * Params:
51
+ * $mq - Breakpoint alias key from $breakpoints.
52
+ *
53
+ * Usage:
54
+ * .title {
55
+ * font-size: 2rem;
56
+ *
57
+ * @include media(xs) {
58
+ * font-size: 1.6rem;
59
+ * }
60
+ *
61
+ * @include media("m-up") {
62
+ * font-size: 2.4rem;
63
+ * }
64
+ * }
65
+ */
66
+ @mixin media($mq) {
67
+ $condition: map.get($breakpoints, $mq);
68
+
69
+ @if $condition {
70
+ @media #{$condition} {
71
+ @content;
72
+ }
73
+ } @else {
74
+ @warn "Oops! Breakpoint `#{$mq}` does not exist in \$breakpoints.";
75
+ }
76
+ }
@@ -0,0 +1,92 @@
1
+ @use "sass:math";
2
+
3
+ /*=============================================
4
+ = Typography / Layout Helpers =
5
+ =============================================*/
6
+
7
+ /**
8
+ * Line-height crop helper
9
+ *
10
+ * Purpose:
11
+ * Visually “crops” the extra space above text caused by line-height,
12
+ * so headings and large text blocks can align better to tight grids.
13
+ *
14
+ * Params:
15
+ * $line-height - The line-height value used on the element (unitless).
16
+ * $capital-letter - Approximate cap-height of the font (unitless, default: 1).
17
+ *
18
+ * Usage:
19
+ * .heading {
20
+ * line-height: 1.2;
21
+ * @include lhCrop(1.2, 0.9);
22
+ * }
23
+ */
24
+ @mixin lhCrop($line-height, $capital-letter: 1) {
25
+ &::before {
26
+ content: "";
27
+ display: block;
28
+ height: 0;
29
+ width: 0;
30
+ margin-top: calc((#{$capital-letter} - #{$line-height}) * 0.5em);
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Desktop column width helper
36
+ *
37
+ * Purpose:
38
+ * Returns a responsive width in vw based on a fixed column grid on desktop.
39
+ * Uses a base column width ($colwidth) relative to $desktop width.
40
+ *
41
+ * Params:
42
+ * $nr - Number of columns to span.
43
+ *
44
+ * Requirements:
45
+ * - $desktop must be defined as the desktop viewport width in px.
46
+ *
47
+ * Usage:
48
+ * .col-3 {
49
+ * width: dCol(3);
50
+ * }
51
+ */
52
+ @function dCol($nr) {
53
+ $colwidth: 80;
54
+ $cols: math.div($colwidth * 100, $desktop);
55
+ $one: ($cols * $nr);
56
+ @return $one + vw;
57
+ }
58
+
59
+ /**
60
+ * Mobile column width helper
61
+ *
62
+ * Purpose:
63
+ * Returns a responsive width in vw based on a 9-column grid on mobile,
64
+ * accounting for left/right gutters.
65
+ *
66
+ * Params:
67
+ * $nr - Number of columns to span.
68
+ *
69
+ * Logic:
70
+ * - $colnr: number of columns (default 9).
71
+ * - $gutter: side gutter in px.
72
+ * - $offset: gutter converted to vw relative to $mobile.
73
+ * - Total usable width = 100vw minus both side offsets.
74
+ * - Column width = usable width / $colnr.
75
+ *
76
+ * Requirements:
77
+ * - $mobile must be defined as the mobile viewport width in px.
78
+ *
79
+ * Usage:
80
+ * .m-col-4 {
81
+ * width: mCol(4);
82
+ * }
83
+ */
84
+ @function mCol($nr) {
85
+ $colnr: 9;
86
+ $gutter: 15;
87
+ $offset: math.div($gutter * 100, $mobile);
88
+ $vw: 100 - ($offset * 2);
89
+ $cols: math.div($vw, $colnr);
90
+ $one: ($cols * $nr);
91
+ @return $one + vw;
92
+ }
@@ -0,0 +1,17 @@
1
+ @use "sass:math";
2
+
3
+ /*=============================================
4
+ = Viewport Unit Helpers =
5
+ =============================================*/
6
+
7
+ @function vh($vh) {
8
+ @return calc(var(--vh, 1vh) * #{$vh});
9
+ }
10
+
11
+ @function tovw($size, $device) {
12
+ @return math.div($size * 100, $device) + vw;
13
+ }
14
+
15
+ @function tovh($size, $height) {
16
+ @return math.div($size * 100, $height) + vh;
17
+ }
@@ -0,0 +1,3 @@
1
+ @import "breakpoints.scss";
2
+ @import "line-height.scss";
3
+ @import "viewport-unit.scss";
@@ -0,0 +1,4 @@
1
+ @charset "UTF-8";
2
+
3
+ @import "base/reset.scss";
4
+ @import "helpers/index.scss";
package/index.js DELETED
@@ -1,30 +0,0 @@
1
- /**
2
- * Legacy setup functions for Stellar Kit.
3
- * These were previously part of Application.js but have been moved out
4
- */
5
-
6
- import { Mouse, Resize, Raf, Scroll } from "./events"
7
- import kitStore from "./kitStore"
8
- import { sniffer } from "./utils"
9
-
10
- export function setupKit({ isSmooth = sniffer.isDesktop, isLocked = false } = {}) {
11
- kitStore.flags.isSmooth = isSmooth
12
- kitStore.flags.isLocked = isLocked
13
-
14
- kitStore.resize = new Resize()
15
- kitStore.raf = new Raf()
16
- kitStore.scroll = new Scroll({ isSmooth })
17
- kitStore.mouse = new Mouse()
18
- }
19
-
20
- export function setSizes(d = kitStore.sizes.d, m = kitStore.sizes.m) {
21
- // update store if values are provided
22
- kitStore.sizes.d = d
23
- kitStore.sizes.m = m
24
-
25
- // set css vars
26
- document.documentElement.style.setProperty("--desktop", d)
27
- document.documentElement.style.setProperty("--mobile", m)
28
- }
29
-
30
- export { kitStore }