@gemboss/gem-framework
v0.10.1
Published
gem-framework của GemTheme (CSS, runtime, token, bộ kiểm) cộng đúng bộ kiểm section gemtheme dùng khi duyệt. Build từ gemtheme, không sửa ở đây.
Readme
@gemboss/gem-framework
gem-framework của GemTheme đóng thành package, cho các repo ngoài monorepo gemtheme (đầu tiên:
gemlabs-gemfactory, tên cũ gemtheme-designlab). Build từ mã nguồn trong repo này; đừng sửa gì ở dist/.
| Cổng vào | Là gì | Nguồn |
|---|---|---|
| @gemboss/gem-framework | token, createGemThemeCss, runtime, manifest, bộ kiểm framework. Từ 0.3.4 manifest khai họ gem-cart-line-item (khối, __media, __details, __title, __variant, __plan, __meta, __remove, __quantity, __price): framework vốn style và adapter Shopify vốn render nó, nên section dùng markup dòng giỏ của framework không còn bị unknown_gem_class | packages/gem-framework/src |
| …/sections | 4 hàm headless.tsx được import (elementOverrides, imageSrcSet, subscriptionPlans, defaultPlan) | packages/gem-sections/src |
| …/section-gate | validateSectionArtifacts: ĐÚNG bộ kiểm section gemtheme dùng khi duyệt. Từ 0.3.0 thêm ba cổng của thư viện cho MỘT section: layoutKnobCheck (+ LAYOUT_ATTRS), slotContractDrift (+ slotsReadBy, slotSourceFromElementTree), cssPairingOrphans (+ classesStyledBy, truyền CSS từ createGemThemeCss). Từ 0.3.2 thêm hai cổng pháp lý: fabricatedProofInSource / fabricatedProofInSchema (điểm, số lượt, người, lời hứa bịa) và inventedTestimonyIn (hằng DEMO_*_QUOTE/REVIEW… làm giá trị khi thiếu dữ liệu). Từ 0.3.3 hai cổng này đọc được tiếng Việt ("4,8/5", "đã bán", "miễn phí vận chuyển", "199k", cả chữ NFD), tra giá trị của DEMO.<key> ở mọi chỗ đọc nó (??, \|\|, toán tử ba ngôi, dòng bị Prettier ngắt), và bỏ qua số lượng hiển thị (reviews_to_show…). Nâng từ 0.3.2 lên có thể làm đỏ section trước đó xanh: đó là lời khẳng định nằm trong DEMO. Từ 0.3.5 bắt thêm chữ ký người Việt (tên kèm nơi chốn như "Chị Lan, Hà Nội", hoặc họ + Thị/Văn như "Nguyễn Thị Hoa"; tiêu đề viết hoa từng chữ thì không), điểm sao ("4,9 ★"), rating: 4.9 / reviewCount: "1.284" trong mảng sản phẩm mẫu, và hằng demo ở nhánh else của toán tử ba ngôi (bound ? [] : DEMO_REVIEWS). Mỗi thứ bịa chỉ báo MỘT lần: tại dòng DEMO nếu dòng đó tự lộ, không thì tại chỗ đọc. "đồng hồ" không còn bị đọc là giá. Từ 0.3.6 bắt lời hứa có thời hạn đứng sau từ khoá ("Dùng thử 30 ngày", "Bảo hành 12 tháng", "Nhận trong 2–4 ngày", "Giao 2 giờ") và "hoàn tiền" không kèm số; "Chính sách hoàn tiền" vẫn là nhãn. Từ 0.3.7 câu hỏi (kết thúc bằng "?") không bao giờ là lời hứa, "không hợp lệ"/số bước/nhịp giao không bị báo, và bắt thêm "trong vòng N ngày", "Giao hàng 2–4 ngày", "Bảo hành 1 đổi 1…", "Try it free for 30 days", "Ships in 24h". Từ 0.3.8 xuất thêm claimIn (câu hỏi "một chuỗi này có phải lời khẳng định, người bịa, hay lời hứa?"). Từ 0.3.9 bắt "cancel anytime", "guaranteed delivery", "huỷ bất cứ lúc nào" (chính sách huỷ gói và giao hàng). Từ 0.3.10 lời từ chối ("cannot be canceled", "Không thể hủy") không bị báo, "giao dịch/giao diện" không phải giao hàng, "Delivery not guaranteed" là lời miễn trừ, một chuỗi chỉ gồm con số viết tắt ("199k", "12k+") mang nhãn trung tính "hardcoded figure" (không phân biệt được giá với số đếm), và stringLiteralsIn(line) thay cho hằng STRING_LITERAL (đã gỡ: một regex /g dùng chung rò lastIndex; 0 chỗ dùng ở gemboss/gemfactory/gemsite). Từ 0.3.11 "Huỷ bỏ lọc…" và "Hủy đơn, gọi chúng tôi bất cứ lúc nào" không bị đọc là chính sách huỷ; "huỷ đăng ký" là bỏ nhận tin (như "Unsubscribe anytime"); SOURCE.json ghi gemtheme_dirty, và npm pack từ chối khi ĐẦU VÀO của gói (packages/, gói này trừ package.json, scripts/lib/) có thay đổi chưa commit hoặc git không trả lời được. Từ 0.3.12 bắt lời hứa hoàn tiền không có chữ "hoàn tiền" liền nhau ("nhận lại toàn bộ tiền", "Hoàn lại 100% tiền", "Trả lại tiền nếu không hài lòng"); lời từ chối ("Không được hoàn lại tiền") và tiền thừa/thối khi thanh toán không bị bắt Từ 0.4.0: demoOutsidePreview(tsx) (chữ mẫu DEMO chỉ hiện trong bản xem trước, tức khi section.demo === true; plan xuất bản không bao giờ mang cờ đó) và schema SectionSpec khai trường demo | packages/gem-sections/src/section-artifacts.ts, section-contract.ts |
| …/settings-schema | validateSettingsSchema | scripts/lib/settings-schema-contract.mjs |
| …/gem-framework.css, …/gem-runtime.js | CSS token mặc định + runtime mọi plugin, dựng sẵn. gem-runtime.js tự chạy khi tải: đăng ký và lên lịch đủ 22 plugin | build |
| …/tokens.css, …/primitives.css, …/sections.css, …/popup-shell.css | Từ 0.8.0: gem-framework.css cắt ba (từ 0.8.3 cắt bốn), nối lại theo đúng thứ tự đó thì bằng gem-framework.css từng byte (build dừng nếu không bằng). tokens.css: biến :root + núm style. primitives.css: reset, phần tử nền, tiện ích, mọi primitive .gem-*. sections.css: khung header/footer, lớp section chung, profile theme. popup-shell.css (0.8.3): khung popup .gem-popup-shell, chỉ trang có popup mới cần tải, đặt sau primitives.css và trước sections.css (thứ tự nối: tokens, primitives, popup-shell, sections). CSS riêng của từng section (section-library/**/gem.css) KHÔNG nằm trong gói | createGemThemeCssParts |
| …/plugins/<key> (…/plugins/<key>.js), …/runtime-core | Từ 0.8.0: mỗi plugin của runtime là một ES module, export function mount(root = document, options?) trả về unmount(), import không làm gì. Cùng dòng mã với gem-runtime.js (một bảng trong runtime.ts), chỉ khác: mọi x.addEventListener( thành __gemListen(x, để unmount gỡ được. runtime-core.js là phần dùng chung: mountPlugin, createGemApi, gemApi | runtime.ts, runtime-modules.ts, runtime-core.js |
Cài
npm i @gemboss/gem-framework # npmjs công khai, cùng scope với @gemboss/ui; không cần tokenTới 29/09/2026 gói tên @chriskoh26/gem-framework và chỉ nằm ở GitHub Packages. Registry đó đòi token kể cả
khi CÀI, nên build trên Railway không kéo được, và GemFactory phải chép tay file .tgz vào vendor/: nó đứng
ở 0.3.2 trong khi gói đã lên 0.4.0 mà không gì báo. Chuyển sang npmjs công khai (Chris chốt scope @gemboss)
thì mọi repo cài theo version, và bị tụt thì thấy ngay ở số version. Mã trong gói là CSS và runtime vốn đã tới
trình duyệt của người mua, cộng bộ kiểm; license: UNLICENSED giữ nguyên (xem được, không có quyền dùng lại).
Kênh next (thử trước khi phát hành)
Mỗi commit lên main của gemtheme mà đổi gói thì có ngay một bản trên GitHub Packages:
@chriskoh26/gem-framework@next, số phiên bản <x.y.z của main>-next.<10 ký tự băm của bản dựng>. Workflow:
.github/workflows/gem-framework-next.yml. Kênh latest trên npm vẫn chỉ đi qua passkey (mục dưới).
Thử ở một repo dùng gói, KHÔNG commit hai thay đổi này:
# .npmrc của repo (hoặc ~/.npmrc), token chỉ cần read:packages
@chriskoh26:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_PACKAGES_TOKEN}
npm install --no-save "@gemboss/gem-framework@npm:@chriskoh26/gem-framework@next"Bí danh giữ nguyên tên @gemboss/gem-framework, nên mã và import của bên dùng không đổi. Bản dựng ghi
gemtheme_sha trong dist/SOURCE.json. Luôn ghi @next (hoặc đúng số): tag latest của gói trên GitHub Packages là
0.4.0 từ 29/09/2026, không phải bản mới nhất.
Phát hành
Một lệnh, chạy trên máy, không dùng GitHub Actions (Chris chốt 30/09/2026: không trả tiền runner):
# 1. nâng version trong dist-packages/gem-framework/package.json qua một PR, merge vào main
# 2. rồi, ở bất kỳ đâu trong checkout gemtheme:
bash scripts/publish-gem-framework.sh
DRY_RUN=1 bash scripts/publish-gem-framework.sh # chạy mọi phép kiểm + npm publish --dry-run, không gửi gìScript build từ một worktree tạm ở origin/main (không bao giờ từ cây đang sửa), từ chối nếu version đã có trên
npm, từ chối nếu dist/SOURCE.json không ghi đúng sha origin/main và gemtheme_dirty: false, tự npm login khi
phiên hết hạn, rồi npm publish. npm hỏi mã từ app xác thực của tài khoản gemboss: đó là bước duy nhất phải có
người. Đổi luật kiểm (manifest, validation,
section-artifacts) là đổi minor: repo dùng package sẽ thấy cảnh báo mới sau khi lên version. Trước khi đổi
version, xem npm view @gemboss/gem-framework versions: phiên khác có thể đã lấy số đó.
Plugin module (0.8.0)
import { mount } from "@gemboss/gem-framework/plugins/countdown";
const unmount = mount(sectionEl); // chỉ gắn trong sectionEl, kể cả chính nó; mặc định cả document
unmount(); // gỡ listener, timer, IntersectionObserver và dấu data-gem-bound-* nó đặtTừ 0.8.2 mỗi module có .d.ts cạnh nó (mount(root?: ParentNode | null, options?: MountOptions): Unmount, key,
readyEvent); MountOptions, Unmount, GemApi nằm ở @gemboss/gem-framework/runtime-core.
- Gắn hai lần lên cùng một root thì chỉ gắn một lần; mỗi lần gọi trả một tay cầm, gỡ hết tay cầm thì plugin mới gỡ.
options.api: hub sự kiện (truyềnwindow.GemThemeđể dùng chung vớigem-runtime.js); mặc định một hub chung của các module. Sự kiệngem:*vẫn bắn trêndocumentnhư runtime.options.window: cửa sổ dùng cho timer/storage/scroll (mặc địnhroot.ownerDocument.defaultView).- Trang đã chạy
gem-runtime.jsrồi mount module lên cùng phần tử thì không gắn đôi: cả hai cùng dùng dấudata-gem-bound-<key>.
Hợp đồng chung với gemsite (0.8.0)
| Plugin | Quyết định |
|---|---|
| countdown | Ghi cả hai bộ ô: data-gem-countdown-days\|hours\|minutes\|seconds (runtime) và data-gem-cd-days\|hours\|mins\|secs (gemsite, Liquid cũ). Có ô giây thì nhảy mỗi giây, không thì mỗi phút, đúng lúc chữ số phút của thời gian còn lại đổi (từ 0.8.2; trước đó tính từ lúc mount nên chậm tới 59 giây). Lúc hết giờ vẫn đúng tới mili giây. Kết thúc: data-gem-countdown-state="complete" là giá trị chuẩn; nghĩa "ended" của gemsite đi bằng data-gem-countdown-ended="true" trên chính timer (như timer theo thời lượng), trừ khi timer là gốc section (để luật band quyết định). gem:countdown:start báo mỗi phút như cũ với bộ đếm phút, một lần với bộ đếm giây; complete một lần. KHÔNG có mốc giả 3 ngày khi thiếu hạn: gemsite chỉ vẽ timer không hạn trong bản xem trước (CountdownBannerOfferBar.tsx), trên store thật không bao giờ; runtime ghi invalid |
| promo-popup | Cùng khoá gem:promo-popup-dismissed:<id>. Đọc cả hai nghĩa: giá trị ở tương lai là "ẩn tới lúc" (runtime ≤ 0.7), giữ ẩn tới lúc đó, và ghi lại MỘT lần thành thời điểm bấm đóng khi trừ đi cửa sổ hiện tại ra một thời điểm đã qua (cửa sổ cũ dài hơn thì giữ nguyên tới khi hết); giá trị ở quá khứ là thời điểm bấm đóng (gemsite), áp cửa sổ data-gem-promo-frequency-days. Ghi từ nay: thời điểm bấm đóng. Hạn "tới lúc" của 0.7 đã qua thì đọc thành thời điểm bấm đóng: popup vắng thêm tối đa một cửa sổ, không bao giờ hiện sớm hơn. Thêm data-gem-promo-trigger = delay (mặc định) | exit_intent (màn cảm ứng rơi về delay) | scroll (data-gem-promo-scroll-percent, 10 tới 90, mặc định 50), mở một lần, ghi data-gem-promo-armed. Trễ mặc định giữ 0 giây của runtime (gemsite: 6) |
| sticky-atc | Bar có data-gem-sticky-atc-threshold (gemsite) thì theo luật gemsite: số đó, không thì biến CSS --gem-sticky-atc-threshold, không thì 360. Không có thì data-gem-sticky-threshold như cũ, mặc định 240. Có cả hai: theo của gemsite |
| :root muted | #656c79 thay #6b7280 (4,88:1 trên nền #f6f6f3, cũ 4,47:1 dưới ngưỡng AA). Đem từ gemsite |
| thanh lọc collection | Không đổi. Bản flex/grid hai khổ của gemsite là gem.css của section collection-filter-toolbar/filter-sort-bar, đã nằm trong section-library của gemtheme, giống từng byte. Gói chỉ mang CSS framework, không mang CSS riêng của section |
Nhật ký thay đổi
- 0.10.1 (09/10/2026): màu
color.warningmặc định#b7791f→#9a661a. Màu cũ chỉ đạt 3,36:1 trên nền mặc định và 3,64:1 trên surface (dưới AA 4,5:1), trong khi section GemFactory dùng nó làm màu chữ và badge vẽaccentTexttrên nó. Màu mới đạt 4,52 / 4,90, giữ tông. Phiên gemfactory phát hiện khi thêm cảnh báo tương phản cho preset. Test mớidefault-colours-meet-aa: mọi màu mặc định dùng làm chữ ≥ 4,5:1 trên cả hai nền. Plan khaicolor.warningriêng (mọi plan sinh từ style pack) thì không đổi: bảng màu pack sửa ở theme-presets (#722). Preview harness của gem-landing-blocks theo màu mới. Vòng tham chiếu chéo nhóm của khoá điện thoại vẫn CHƯA chặn (ghi ởreadPhoneTokens). - 0.10.0 (08/10/2026, gộp 0.9.10 và 0.9.11 chưa phát hành): phần còn lại của yêu cầu #4 GemFactory (Figma CRO, issue
gemfactory #95). Plan không khai khoá nào dưới đây thì CSS tính ra như cũ (mỗi
var()lùi về đúng giá trị cũ).- Line-height tiêu đề: opt-in
data-gem-heading="section"(1.1),.gem-section__headingnền (1.04) và của Dawn parity (1.08) đọc--gem-font-heading-line-height, tứctypography.headingLineHeight. Khoá này ĐÃ CÓ trong hai style pack (graphic-editorial0.95,warm-mineral-spf1.2) mà trước giờ không tới tiêu đề section: store dùng hai pack đó đổi line-height tiêu đề section và hero theo đúng khoá đã khai. Giá trị không phải line-height bị bỏ (biến khai sai làmvar()bỏ qua fallback và rơi về 1,5 của body),validateGemTokensbáoinvalid_line_height_token. Luật riêng của biến thể Dawn có cỡ px cố định (hero split-proof 46px/1.08, compact-collection…) giữ line-height riêng. - Cỡ tiêu đề theo thang chữ, CHỈ khi plan khai
typography.basehoặcscale: phát--gem-section-heading-size(=--gem-h2× núm heading-scale),--gem-hero-heading-size(--gem-h1, chỉ luật hero h1/h2 của Dawn đọc) và bản-mobile(không lớn hơn--gem-heading-phone-cap, trần điện thoại của PROFILE: Dawn khaiclamp(2rem, 11vw, 3.6rem)trên:root, profile nền lùi vềclamp(1.75rem, 9vw, 2.75rem)như #587). Mọi luật tiêu đề section đọc chúng. - Khoá điện thoại:
spacing.<bậc>Mobile(cả 10 bậc) vàlayout.sectionMobile(so khoá bỏ qua hoa thường và dấu nối) khai lại biến gốc trong khối@media (max-width: 749px)củabaseMobile. Spacing nhân núm mật độ; giá trị kiểm hình dạng nhưmd(0 trần thành0px); giá trị không đơn vị, hoặc đọc biến của chính nhóm nó (khai lại trên:rootlà vòng lặp, padding về 0), bị bỏ vàvalidateGemTokensbáoinvalid_phone_token.sectionMobilecòn đặt--gem-layout-section-mobiletrên:rootdesktop: padding ≤760px của profile nền dùng nguyên giá trị thay vì desktop × 0,72. Không có khoá điện thoại chopage/gutter(bề rộng điện thoại của profile nền không đọc chúng). Ở lane một stylesheet (createGemThemeCss),:rootcủa Dawn đứng sau token và khoálayout.*như trước. - Opt-in eyebrow có
font-size: var(--gem-text-sm)(mặc định 13,1px, luật nền 13px). - Xuất
optionalGemTokenKeys(manifesttoken_groups[].optional_keysđọc từ đây, gồm khoá nút của 0.9.10),gemMobileTokenCssVars,gemPhoneTokenTarget,GEM_HEADING_SIZE_CSS_VARS,GEM_LINE_HEIGHT_TOKEN_KEYS: bản chép ở gemsite (theme-plan-style.ts) và cổng preset của GemFactory đối chiếu với chúng thay vì chép tay.
- Line-height tiêu đề: opt-in
- 0.9.11 (08/10/2026): marquee còn MỘT định nghĩa. Trước đó primitives.css có hai: bản của gemtheme #124 (một track
nhân đôi con, dịch -50%) nằm TRÊN bản #79 (
__viewport+ hai track, dịch -100%) nên thua mọi thuộc tính cả hai cùng đặt (min-width,animation), và hợp đồng của #124 chưa từng chạy. Giữ hợp đồng #79, đúng thứ clone kit của gemcapture và GemFactory dựng, cộng những gì của #124 vẫn đang có hiệu lực: viềnborder-block,will-change, dừng khi rê lên cả.gem-marquee, đảo chiều bằngdata-gem-marquee-reverse(bên cạnh.gem-marquee--reverse). Bỏ dònganimation-durationcủa marquee trong khối reduced-motion của ambient: ở đó track đãanimation: none. Đo Chromium (6 kiểu dựng marquee, 390 và 1280, có và không reduced-motion, rê chuột 3 chỗ, đồng hồ animation ghim ở 1234 ms): 516.832 giá trị tính ra, 56 lệch, cả 56 làanimation-durationdưới reduced-motion trên track cóanimation-name: noneở cả hai bản, tức không có gì chạy; bản cố ý hỏng (bỏwill-changevà hook đảo chiều bằng thuộc tính) lệch 328. Test mớione-owner-per-selector: selector viết hai lần trong cùng file, cùng @media, có thuộc tính đè nhau thì chỉ được giảm (còn 28 ca, liệt kê trong test). - 0.9.10 (08/10/2026): token nút tuỳ chọn (
GEM_BUTTON_TOKEN_KEYS, hình dạng ởGEM_BUTTON_TOKEN_SHAPES), cả bốn tên đã có mà chưa ai vẽ:radius.button(--gem-radius-button, scale theo--gem-style-radius; validators và generation-jobs đã dùng tên này),typography.buttonWeight,typography.buttonTransformvàtypography.buttonLetterSpacing(schema style pack;luxury-editorialkhai nút in hoa 0.18em từ trước, giờ mới hiện). Không khai thì nút vẽ y như trước (đo Chromium: 90/90 giá trị tính ra trùng bản cũ ở dawn-parity và ở morph). Bo góc là của riêng nút, nên nút pill không kéo thẻ thành pill; dawn-parity hết ép nút vuông / đậm 400 khi có token. In hoa và giãn chữ không nằm trong.gem-button(UA cho<button>none, còn<a>kế thừa):createGemTokenCssthêm luật:where(...)độ ưu tiên 0 chỉ khi có token (gemButtonCaseCss, export cho storefront tự viết token CSS), nên luật CTA của section vẫn thắng. Nút dạng link không nhận độ đậm / chữ hoa của token. Giá trị sai hình dạng ("pill", rỗng,"semibold") không được phát,validateGemTokenscảnh báoinvalid_button_token; khoá snake_case của editor đi qua cùng cổng. Manifest cóoptional_keys. Nguồn: GemFactory Global Style 08/10/2026 (339 trang store hàng đầu: CTA pill 39%, in hoa 47%, giãn chữ 18%). - 0.9.9 (08/10/2026):
gemFrameworkVersion(vàframework_version/gem_framework_versiontrong manifest) bằng đúng phiên bản của gói. Từ 0.4.0 tới 0.9.8 nó đứng yên ở"0.1.0", nên trang Framework của gemboss hiện sai phiên bản và GemFactory phải tự đọc phiên bản npm. Test trongindex.test.tsđỏ khi hai giá trị lệch nhau. Không đổi CSS hay runtime. - 0.9.8 (06/10/2026): biến chỉ được tính là "khai trên :root" khi MỌI vế của selector đúng là gốc (
:root,html,:host, có thể kèm điều kiện như:root[data-theme="dark"]), không bao giờ là con của nó. Bản 0.9.7 chỉ xét đầu selector, nên một luật:root .gem-row { --gem-row-auto-sm: … }sẽ biến biến nội bộ thành công khai. Stylesheet hiện tại chưa có luật nào như vậy, nên kết quả không đổi (review của gemboss #3842). ThêmgemPublicCssVariablesFrom(css). - 0.9.7 (06/10/2026):
validateGemFrameworkCssnhận mọi biến CÔNG KHAI của chính framework, đọc từ stylesheet của nó ở cả hai profile: biến khai trên:root(--gem-card-radius,--gem-media-radius,--gem-ease-soft,--gem-grid-row-gap…) và điểm mở chỉ được đọc kèm dự phòng (--gem-card-background,--gem-marquee-duration…). Trước đó danh sách ghép tay bỏ sót 25 biến như vậy, nên section đọc đúng biến framework vẫn nhậnunknown_gem_css_variable(GemFactory gap #29). Biến nội bộ mà framework chỉ khai trên class của nó (--gem-row-auto-*,--gem-stars-*,--gem-section-bg) vẫn bị báo, và tên gõ sai cũng vậy. - 0.9.6 (06/10/2026):
media-gallerycó chế độ ảnh chính + thumbnail (gallery có[data-gem-media-main]): bấm thumbnail thì ảnh chính nhậndata-gem-media-src/-altcủa nó, bỏsrcset,aria-currentchuyển sang thumbnail đó, và phátgem:media:select{ index }nhưbehavior.jscủa section. Phần tử chỉ khớp theo class mà bọc một hook[data-gem-media-gallery]không còn bị gắn thành gallery thứ hai (trước đó nó đánh số lại thumbnail bằng cách đếm mọi<img>, kể cả ảnh chính). Gallery không có ảnh chính chạy như cũ. - 0.9.5 (06/10/2026):
sticky-atclàm đủ việc củabehavior.jssection: giá trên thanh đổi theo biến thể khách chọn (data-gem-variant-pricetrên từng<option>), và form KHÔNG cóaction(thanh headless) thì chặn submit, phátgem:cart:addvới biến thể đang chọn (không phải biến thể đầu ghi trên nút). Form cóaction(Liquid/cart/add) vẫn để trình duyệt gửi như cũ. Trước đó plugin chỉ báogem:sticky-atc:submit, nên trang nạp plugin mà không có bản của section thì bấm thêm vào giỏ là tải lại trang. - 0.9.4 (06/10/2026): nút tạm dừng của
hero-slideshowđổi chữ theo trạng thái nhưbehavior.jscủa section từng làm: khi dừng nút ghidata-gem-hero-play-label(mặc địnhPlay), khi chạy lại ghi đúng chữ ban đầu của nút (nên nhãn đã dịch không bị đè). Nút có phần tử con (icon) giữ nguyên markup, chỉ đổiaria-pressed. Đây là chỗ lệch cuối cùng giữa plugin và section, để section giao hành vi cho plugin (một chủ, Chris 06/10/2026). - 0.9.3 (06/10/2026): CSS Dawn parity chỉ đặt
gap !importantcho lưới thẻ quick-view (.gem-product-card-grid--quick-view-grid), không cho mọi.gem-product-card-grid__grid. Khớp bản chép của gemsite; store không đổi gì. - 0.9.2 (06/10/2026):
reveal-on-scrollbỏ quadata-gem-reveal="none"và"default"(hai giá trị "không áp gì" của knob reveal): section hiện ngay, không ẩn chờ cuộn. Trước đó section đặtnonevẫn bị ẩn như mọi section reveal. - 0.9.1 (06/10/2026): plugin
reveal-on-scrollkhông còn giấu section khách đang thấy. Section đã nằm trong màn hình lúc tải được hiện tại chỗ (trước đó bị đặtreadyrồi mới hiện lại, nên phần đầu trang chớp tắt), và khi trình duyệt bậtprefers-reduced-motion: reducemọi section hiện ngay, không ẩn chờ cuộn. Hai luật lấy từcomponents/revealOnScroll.jscủa gemsite, để gemsite dùng được plugin của gói. - 0.9.0 (05/10/2026):
claimIn(và mọi cổng dùng nó:fabricatedProofInSource, làn mã sinh theme củacheck-fabricated-proof) bắt lời hứa phí ship bằng 0 không có chữ "free" (shippingZeroPromiseIn: tách câu, bỏ ký hiệu đầu dòng, đòi CẢ câu khớp một dạng có tên): "No extra shipping.", "No additional shipping costs", "No shipping fees on this order", "Shipping (costs) included" đứng riêng, "Includes shipping", "Zero shipping fees", "Không mất/thu phí ship", "Không phí vận chuyển", "Đã (bao) gồm phí ship". Chỉ dạng lời hứa không mơ hồ: lời từ chối, câu hỏi, nhãn giỏ ("Total (shipping included)", "Tổng (không tính phí vận chuyển)"), nhãn tuỳ chọn ("Include shipping protection") và trạng thái ("No shipping fees have been configured") vẫn sạch; bộ 130 ca trongsection-contract.test.ts. Nguồn: mẫu post-purchase bonus-steps của gemboss in "No extra shipping." cho mọi store (GemFactory gap #21). Minor vì là luật kiểm mới: chữ trước đó xanh có thể đỏ. GemFactory ghim^0.6.0nên chỉ có luật này khi nâng phiên bản ở PR riêng. - 0.8.3 (05/10/2026): sửa các lỗi popup ghi trong review gemboss PR #3758. Khung popup
.gem-popup-shellra khỏiprimitives.cssthànhpopup-shell.cssriêng (export./popup-shell.css):primitives.cssnhẹ đi 9.058 byte thô, 1.194 byte gzip;gem-framework.cssvẫn đủ luật, khung nằm giữa primitives và sections (đo Chromium 390 và 1280, 15 ca: giống hệt từng điểm ảnh).createPrimitiveCss()không còn khung: ai dựng CSS từ hàm đó mà vẽ popup thì thêmGEM_POPUP_SHELL_CSS. Pluginpromo-popupbỏ qua popup đã mangdata-gem-bound-promo-popup(popup email-capture tự chạybehavior.js), vàapi.promoPopup.openchỉ mở popup nó đã gắn, mở cả dialog, tính là lần mở của lượt xem trang (gỡ trigger đang chờ). Countdown chế độvisiblebắngem:countdown:startmột lần (trước đây mỗi lần chạy tiếp) và gỡ IntersectionObserver ngay khi hết giờ hoặc dừng. - 0.8.2 (05/10/2026): countdown không có ô giây (trang nội dung gemboss chỉ ghi ngày/giờ/phút) đổi chữ số phút
đúng lúc thời gian còn lại qua mốc phút, không còn chậm tới 59 giây vì nhịp 60 giây tính từ lúc mount (gemsite #780
tìm ra). Có ô giây thì vẫn mỗi giây; lúc hết giờ vẫn đúng;
gem-runtime.jsđổi theo vì cùng một bảng. Gói kèm khai báo kiểu:dist/plugins/<key>.d.tsvàdist/runtime-core.d.ts,typestrongexportscủa./plugins/*và./runtime-core, nên repo TypeScript import@gemboss/gem-framework/plugins/<key>không cầndeclare module. - 0.8.1 (05/10/2026): bỏ
.gem-footerkhỏi danh sách root của các núm layout (:is(.gem-section, …, .gem-slideshow), 68 chỗ). Không còn gì phát ra class đó từ khi hai footer cũ bị xoá (gemtheme #660, library 1.38.0); hai section đó cũng mang.gem-sectionnên luật vẫn với tới chúng qua root kia. Không đổi hành vi. - 0.8.0 (05/10/2026): plugin module (
plugins/*,runtime-core), CSS cắt ba (tokens.css,primitives.css,sections.css), hợp đồng chung countdown / promo-popup / sticky-atc với gemsite, muted#656c79.gem-runtime.jsvẫn tự chạy, vẫn đủ 22 plugin; hành vi chỉ đổi ở ba plugin trên, theo bảng trên.
