studio-engine 0.38.0 → 0.39.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d497957db77f7b61823d1d12422672bf4339d71381163b79a6c42d705abf3492
4
- data.tar.gz: e31717daca9b7722e4b9e87d44a507d9ec8c8055421e192bb3de78aa31175542
3
+ metadata.gz: bb273757252724d41962bac1a7c6318c0d04006ff41c1ba29735ad413c0b57fc
4
+ data.tar.gz: 618a5a39311ac9a316c649a502b44fa6540e8dc70c776fc3c3b2c17455473479
5
5
  SHA512:
6
- metadata.gz: 6f75c493e66ebae0fd31571d237fa6a4f1546e9c50143382a4f54bfe88d5614c1f582af6d758cafe93ffc47b744b01d875a280de92823012bf224f028859627d
7
- data.tar.gz: dfa14e14dbe5796816a0247bd965da477b69e27e8c81750f554e1d2d497fbf486da5d14f1ae900aa26b28dcfe22c14a51f2e83ead2089d98f9667b0142d43b6d
6
+ metadata.gz: 9b7bcf2d4dca51566942a2179a6f06e6c4a70293acab99b5a747a76785d9cba69cf51e4992e02410e82e9602f6c317605866ed1bd5b59bbf452954eae5d620aa
7
+ data.tar.gz: 1136131a5250c8a014edeb8ddd602c23dced903a82b99d3c57eb3bf27dd2394b735c993d08a559ff6132b8ffcdf337a4d6a91d8f4396ef8ab83c7679b7e2c4d7
data/CHANGELOG.md CHANGED
@@ -2,7 +2,41 @@
2
2
 
3
3
  The format is [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html) — `MAJOR.MINOR.PATCH`. Consumer Rails apps install the released RubyGems package with `gem "studio-engine", "~> 0.6"`; bumping the gem version and updating consumer lockfiles is a release.
4
4
 
5
- ## Unreleased
5
+ ## 0.39.0 — 2026-08-11
6
+
7
+ ### Removed
8
+
9
+ - **`--studio-bars-h` is gone.** 0.33 advertised this CSS custom property as the
10
+ contract between the bar stack and a consumer's navbar, and the navbar's
11
+ sticky `top` read it. It no longer exists: the stack now sits in normal flow
12
+ directly above the navbar and simply occupies its own height, so a navbar
13
+ sticky at `top: 0` starts underneath the bars with nothing to compute.
14
+
15
+ **No consumer breaks.** A navbar still spelling `top: var(--studio-bars-h,
16
+ 0px)` resolves to its `0px` fallback — exactly where the new layout wants it —
17
+ so an adopting app self-heals with no change. Verified against
18
+ `mcritchie-studio` (which carries that spelling) and `turf-monster` (which
19
+ never adopted the stack).
20
+
21
+ ### Fixed
22
+
23
+ - **The navbar no longer jumps on load or navigation.** The removed property was
24
+ published twice and the two publishers could disagree: the server rendered an
25
+ ESTIMATE (bar count × a fixed unit) and a `ResizeObserver` then MEASURED the
26
+ real height and overwrote it. The header painted at the estimate and moved
27
+ when the measurement landed, moved again whenever a webfont changed a bar's
28
+ real height, and during a view transition the outgoing and incoming headers
29
+ composited at two different tops — so the navbar visibly drew twice. A
30
+ position only CSS can set cannot disagree with itself.
31
+
32
+ The estimate, the `ResizeObserver`, and the inline `<style>`/`<script>` the
33
+ stack used to emit are all gone. Reported from QA by the operator.
34
+
35
+ **Trade, recorded deliberately:** the bars now scroll away rather than
36
+ pinning. Two pinned siblings of unknown height cannot stack in CSS alone —
37
+ one must measure the other, and that measurement *is* the defect.
38
+
39
+ ## 0.38.0 — 2026-08-10
6
40
 
7
41
  **The email registry becomes the email catalog.** 0.37 gave every app a shared
8
42
  page for its transactional email banners. This folds in the rest — **what each
@@ -38,13 +38,15 @@
38
38
  %>
39
39
 
40
40
  <header x-data="{ scrolled: false }" <%= '@scroll.window="scrolled = scrolled ? (window.scrollY > 5) : (window.scrollY > 60)"'.html_safe unless is_preview %>
41
- <%# top offsets by whatever studio/banners/_stack rendered above us. The
42
- stack publishes --studio-bars-h on :root; with no stack the fallback
43
- 0px makes this identical to the old `top-0`, so an app that has not
44
- adopted the stack is unaffected. The navbar never learns WHICH bars
45
- rendered only how tall they are. %>
46
- style="<%= "top:var(--studio-bars-h, 0px);" unless is_preview %>"
47
- class="<%= is_preview ? 'bg-page' : "#{'vt-pinned-header ' if pin_header}sticky z-50 bg-page transition-shadow duration-300" %>"
41
+ <%# top-0, a STATIC value, and never a custom property. Any bars render as
42
+ this header's sibling in normal flow (studio/banners/_stack), so they
43
+ already occupy their own height above it and there is nothing to
44
+ offset by. An offset read from a runtime-published property is what
45
+ made this header jump: it painted at a server estimate, moved when the
46
+ measurement landed, moved again on a webfont swap, and drew twice
47
+ during a view transition composited at two different tops. Only CSS
48
+ can set this top now, so none of that is reachable. %>
49
+ class="<%= is_preview ? 'bg-page' : "#{'vt-pinned-header ' if pin_header}sticky top-0 z-50 bg-page transition-shadow duration-300" %>"
48
50
  :class="scrolled && 'shadow-lg border-b border-subtle is-scrolled'">
49
51
  <style>
50
52
  .user-nav-col { width: 14rem; }
@@ -24,9 +24,12 @@
24
24
  }
25
25
  }
26
26
 
27
- # INTRINSIC height — content decides, and studio/banners/_stack MEASURES the
28
- # result. An earlier cut declared a fixed height so the stack could compute its
29
- # total server-side; that was rejected deliberately, because it cannot express
27
+ # INTRINSIC height — content decides, and the LAYOUT absorbs the result: the
28
+ # stack sits in normal flow, so a bar simply occupies the height it needs and
29
+ # nothing has to measure it. (It once did measure, publishing --studio-bars-h;
30
+ # that jumped, and 0.39.0 removed it.) An earlier cut declared a fixed height
31
+ # so the stack could compute its total server-side; that was rejected
32
+ # deliberately — and the reason still holds — because it cannot express
30
33
  # a bar that needs to be taller, or two bars stacked, without every consuming
31
34
  # app being reworked. The apps should respond to what the bar needs, not the
32
35
  # other way round.
@@ -12,22 +12,31 @@
12
12
  0, 1 or 2, and growing. Nesting each new bar inside the navbar coupled two
13
13
  unrelated components and meant every new bar edited the navbar. Here the bars
14
14
  do not know the navbar exists, and the navbar does not know which bars
15
- rendered — only how tall they are, via --studio-bars-h.
15
+ rendered — it simply starts below whatever space they take.
16
16
 
17
- HOW THE HEIGHT IS KNOWN. The stack MEASURES itself and publishes the result, so
18
- a bar that needs to be taller — or a second bar, or a third just works, and no
19
- consuming app is reworked to allow it. The apps respond to what the bars need.
17
+ HOW THE SPACE IS RESERVED. By the LAYOUT, and by nothing else. The stack sits
18
+ in normal flow directly above the navbar, so the bars occupy their own height
19
+ the way any block does, and the navbar sticky at top:0 starts underneath
20
+ them with no offset to compute. A taller bar, or a second, or a third, simply
21
+ takes more room and pushes the navbar down. Nothing measures, nothing
22
+ publishes, nothing repaints.
20
23
 
21
- A server-rendered estimate (count × --studio-bar-unit) paints first so the
22
- common case has no flash, then a ResizeObserver replaces it with the truth on
23
- the same frame. An earlier cut declared a fixed bar height to avoid the
24
- observer; that was rejected because it could only ever express the standard
25
- case, which is the case that needed no help.
24
+ This replaced a measured custom property (--studio-bars-h: a server-rendered
25
+ estimate of count times a fixed unit, overwritten by a ResizeObserver reading
26
+ the real height). It worked, and it JUMPED: the header painted at the estimate
27
+ and moved when the measurement landed, then moved again whenever a webfont
28
+ changed the bar's real height and during a view transition the outgoing and
29
+ incoming headers composited at two different tops, so the navbar visibly drew
30
+ twice. A position that only CSS can set cannot do any of that.
26
31
 
27
- The property is published on :root from an inline <style>, deliberately. A
28
- custom property set on this element would be invisible to the navbar, because
29
- custom properties inherit DOWN, not ACROSS to siblingsand putting it on
30
- <body> would need every host to change its layout.
32
+ THE TRADE, recorded so it is a decision and not a regression: the bars scroll
33
+ away with the page instead of pinning. Only the navbar stays. Two pinned
34
+ siblings of unknown height cannot stack in CSS alone one has to measure the
35
+ other so pinning the bars is what cost the offset variable. The navbar keeps
36
+ its pin; the bars are ambient labels, seen on every page load because a Turbo
37
+ visit lands at the top. An overlay that must clear the chrome should position
38
+ off --nav-bottom (published in layouts/studio/_head), which already reports the
39
+ header's live bottom edge and accounts for chrome above it.
31
40
 
32
41
  Locals (all optional):
33
42
  preview — true inside a navbar-preview render; renders nothing, so a
@@ -61,42 +70,17 @@
61
70
  end
62
71
  end
63
72
 
64
- # count { } and not count(true): show_impersonation is a truthy USER, not the
65
- # literal true, and count(true) compares with ==. That published a one-bar
66
- # height for a two-bar stackthe navbar would have sat under a bar.
67
- bar_count = [show_environment, show_impersonation].count { |bar| bar }
73
+ # No count is kept. The old code counted bars to compute an offset, and had to
74
+ # use count { } rather than count(true) because show_impersonation is a truthy
75
+ # USER object, not the literal true a mistake that once published a one-bar
76
+ # height for a two-bar stack. Normal flow needs no count at all: whatever
77
+ # rendered takes the room it needs.
78
+ any_bar = show_environment || show_impersonation
68
79
  %>
69
- <% if bar_count.positive? %>
70
- <%# First-paint estimate only the observer below replaces it with the measured
71
- height. Expressed against a token so an app that retunes the unit still gets
72
- a sensible pre-measurement value. %>
73
- <%= tag.style safe_join([":root{--studio-bars-h:calc(#{bar_count} * var(--studio-bar-unit, 47px))}"]) %>
74
- <div class="studio-bar-stack sticky top-0 z-[60] w-full" data-studio-bar-stack><%= bars %></div>
75
- <script>
76
- // Publishes the stack's REAL height, so the navbar offsets by what the bars
77
- // actually need. Idempotent and re-bound on Turbo navigation — a stale
78
- // observer pointed at a detached node silently stops updating, and the
79
- // navbar would then sit at yesterday's offset.
80
- (function () {
81
- var apply = function () {
82
- var el = document.querySelector("[data-studio-bar-stack]");
83
- if (!el) {
84
- document.documentElement.style.removeProperty("--studio-bars-h");
85
- return;
86
- }
87
- if (window.__studioBarObserver) window.__studioBarObserver.disconnect();
88
- var publish = function () {
89
- var h = Math.round(el.getBoundingClientRect().height);
90
- if (h > 0) document.documentElement.style.setProperty("--studio-bars-h", h + "px");
91
- };
92
- publish();
93
- if (window.ResizeObserver) {
94
- window.__studioBarObserver = new ResizeObserver(publish);
95
- window.__studioBarObserver.observe(el);
96
- }
97
- };
98
- apply();
99
- document.addEventListener("turbo:load", apply);
100
- })();
101
- </script>
80
+ <% if any_bar %>
81
+ <%# Normal flow, deliberately. No sticky, no z-index, no measured offset: the
82
+ navbar is the only pinned chrome, so the bars simply take their own height
83
+ above it. Not sticky also keeps the paint order right — the pinned navbar
84
+ is positioned, so it draws over these bars as they scroll up behind it. %>
85
+ <div class="studio-bar-stack w-full" data-studio-bar-stack><%= bars %></div>
102
86
  <% end %>
@@ -1,3 +1,3 @@
1
1
  module Studio
2
- VERSION = "0.38.0"
2
+ VERSION = "0.39.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: studio-engine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.38.0
4
+ version: 0.39.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex McRitchie