qss 0.1.0 → 0.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a8e93da5bd3a65eb8804f5416a1a0315f23110bb6027703c71974b7fa4443d73
4
- data.tar.gz: d00e38e05740739f64aff39d17f491558f76a0556ec71abaa7781c278059c60c
3
+ metadata.gz: 8ede42a34241c427ed061678140580661f4dbb494ded598e312e64e34fdcf211
4
+ data.tar.gz: 813b25c7cf09e631881a0c410d3625dd43f556aa709c165be4fdc33a0d2ce73d
5
5
  SHA512:
6
- metadata.gz: dc74ce934ef4007d538d184b46822b89bbb3e6ee1c0a2ff90900ae69729434e5413dac0380c33976ae2ba43cf01a9a30e6b67140f4055fdadf758781b31f7be1
7
- data.tar.gz: aa18e0742c0dfc5bb26067fed5dff96f52b28f0e495634b9aac87e1ead4ed60ae5124cae80b12f5b4fce43f44f2a06f7d8f71f2f7c8e99f0c68b3147f4799931
6
+ metadata.gz: 134099b4edc02e6fec980a1b5aff0b9e23e776c7e848d1ec156c6e7c6b88b724c4b219c7fffff2fc8d4a498beb185c2062be2bd01582b68a17ee4f093ada00b3
7
+ data.tar.gz: c0c7cfa4e64b5cd476234cb421544f4ef038b90f6d37b9ed22a6d4ca18a2d16df6d0a2b2379819ae2f4fc83adb3b70d7760da7309d09c7b94c17d369b684be99
data/CHANGELOG.md CHANGED
@@ -3,7 +3,25 @@
3
3
  All notable changes to the qss gem. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/);
4
4
  versions follow [Semantic Versioning](https://semver.org/) (before 1.0, minor versions may break the API).
5
5
 
6
- ## [0.1.0] - unreleased
6
+ ## [Unreleased]
7
+
8
+ ## [0.1.2] - 2026-10-05
9
+
10
+ ### Changed
11
+ - Gem metadata only, no code changes: the summary and description now explain why QSS exists (agents and flex/grid
12
+ layout, addressable structure, static auditing and its limits, the origin in Parapet), and the gem links to
13
+ `docs/DESIGN_HISTORY.md` as its documentation. The rubygems.org page reads these from the latest release.
14
+
15
+ ## [0.1.1] - 2026-10-05
16
+
17
+ ### Fixed
18
+ - `contain-layout` and `qss-contain-layout` now have a rule (`contain: layout`) in `qss_core.css`. The README
19
+ documents the class for structural frames and the native auditor mandates it, but the stylesheet never defined it,
20
+ so a frame that satisfied the auditor received no containment. Anyone already using the class will now get layout
21
+ containment on those frames: check that absolutely and fixed-positioned descendants still position as expected.
22
+ Covered by a spec on the compiled stylesheet and by three checks in the browser render check (21 in total).
23
+
24
+ ## [0.1.0] - 2026-09-25
7
25
 
8
26
  First release, extracted from the Parapet project.
9
27
 
data/README.md CHANGED
@@ -6,7 +6,7 @@ thousandth of the canvas width, computed by CSS alone. QSS ships compiled CSS, i
6
6
  JavaScript engine, and two **auditors** that check views against QSS's rules. It includes a Rails engine for the
7
7
  asset pipeline; the auditors and configuration also work in plain Ruby.
8
8
 
9
- > **Status: 0.1.0, early.** QSS was extracted from the Parapet project (a Rails development tool),
9
+ > **Status: 0.1.x, early.** QSS was extracted from the Parapet project (a Rails development tool),
10
10
  > which is its first consumer. Expect names and APIs to change before 1.0. See [CHANGELOG.md](CHANGELOG.md).
11
11
  > Why QSS is designed this way: [docs/DESIGN_HISTORY.md](docs/DESIGN_HISTORY.md).
12
12
 
@@ -60,6 +60,7 @@ const QSSEngine = (function() {
60
60
  'centered',
61
61
  'clickable',
62
62
  'contain',
63
+ 'contain-layout',
63
64
  'contrast',
64
65
  'cover',
65
66
  'cursor-col-resize',
@@ -71908,6 +71908,16 @@ html, body {
71908
71908
  object-fit: contain;
71909
71909
  }
71910
71910
 
71911
+ /* Layout containment for structural frames: the class the README documents and the native auditor mandates on
71912
+ frames whose id names a structural word (header, nav, sidebar, ...). Both spellings the auditor accepts. */
71913
+ :is(#qss-canvas) .contain-layout {
71914
+ contain: layout;
71915
+ }
71916
+
71917
+ :is(#qss-canvas) .qss-contain-layout {
71918
+ contain: layout;
71919
+ }
71920
+
71911
71921
  :is(#qss-canvas) .cover {
71912
71922
  object-fit: cover;
71913
71923
  }
@@ -411,6 +411,11 @@ $qss-scope: if($qss-portal-root == "", $qss-anchor, "#{$qss-anchor}, #{$qss-port
411
411
  @include qss-dsl-boost(".grid") { display: grid; }
412
412
  @include qss-dsl-boost(".table-fixed") { table-layout: fixed; }
413
413
  @include qss-dsl-boost(".contain") { object-fit: contain; }
414
+
415
+ /* Layout containment for structural frames: the class the README documents and the native auditor mandates on
416
+ frames whose id names a structural word (header, nav, sidebar, ...). Both spellings the auditor accepts. */
417
+ @include qss-dsl-boost(".contain-layout") { contain: layout; }
418
+ @include qss-dsl-boost(".qss-contain-layout") { contain: layout; }
414
419
  @include qss-dsl-boost(".cover") { object-fit: cover; }
415
420
 
416
421
  /* Visual Atmosphere */
data/config/grammar.yml CHANGED
@@ -48,6 +48,7 @@ adjectives:
48
48
  - justify-between
49
49
  - grid
50
50
  - table-fixed
51
+ - contain-layout
51
52
  - w-full
52
53
  - h-full
53
54
  - w-f
@@ -30,6 +30,8 @@ HTML = <<~HTML
30
30
  <div id="vertical" class="qss vertical"></div>
31
31
  <div id="flex1" class="qss flex-1"></div>
32
32
  <div id="padded" class="qss p[4]"></div>
33
+ <div id="contained" class="qss contain-layout"></div>
34
+ <div id="contained-prefixed" class="qss qss-contain-layout"></div>
33
35
  <div id="host" class="qss host-static"></div>
34
36
  <div class="foreign"><div id="in-foreign" class="qss"></div></div>
35
37
  <div id="nearer-container" class="qss w[100] h[10]" style="container-type: inline-size"><div id="deep" class="qss"></div></div>
@@ -49,6 +51,9 @@ CHECKS = [
49
51
  [ "flex-1 beats the reset's flex-shrink", "flex1", "flex", "1 1 0%" ],
50
52
  [ "p[N] beats the reset (4 units on a 1000px canvas)", "padded", "padding", "4px" ],
51
53
  [ "a host app's plain class does not override structure", "host", "position", "absolute" ],
54
+ [ "contain-layout gives layout containment", "contained", "contain", "layout" ],
55
+ [ "qss-contain-layout gives layout containment", "contained-prefixed", "contain", "layout" ],
56
+ [ "a bare actor has no containment", "bare", "contain", "none" ],
52
57
  [ "foreign content is left alone", "in-foreign", "position", "static" ]
53
58
  ].freeze
54
59
 
@@ -25,6 +25,7 @@ module QSS
25
25
  'centered',
26
26
  'clickable',
27
27
  'contain',
28
+ 'contain-layout',
28
29
  'contrast',
29
30
  'cover',
30
31
  'cursor-col-resize',
@@ -120,7 +121,7 @@ module QSS
120
121
 
121
122
  PAYLOAD_TAGS = ["p", "h1", "h2", "h3", "h4", "h5", "h6", "span", "label", "blockquote", "li"].freeze
122
123
 
123
- ADJECTIVE_CATEGORIES = {"positional"=>["detached", "floating", "stuck", "absolute", "pos-x", "pos-y", "pos-r", "pos-b", "pos"], "flow_relative"=>["anchored", "relative"], "layout"=>["vertical", "horizontal", "centered", "v-mid", "h-mid", "v-start", "v-end", "h-start", "h-end", "spaced", "around", "evenly", "flex", "flex-col", "flex-row", "flex-wrap", "flex-nowrap", "flex-1", "flex-auto", "flex-none", "items-center", "justify-center", "justify-between", "grid", "table-fixed", "w-full", "h-full", "w-f", "h-f"], "typographic"=>["italic", "uppercase", "lowercase", "underline", "nowrap", "truncate", "font-mono", "font-sans", "font-medium", "font-bold", "font-black", "text-size", "leading", "tracking", "tracking-tighter", "tracking-wide", "tracking-wider", "tracking-widest", "icon-size"], "visual"=>["bg", "border", "border-t", "border-b", "border-l", "border-r", "rounded", "rounded-full", "shadow", "opacity", "blur", "bright", "contrast", "grayscale", "hue", "invert", "saturate", "sepia", "blend", "contain", "cover", "visible", "invisible", "bg-alpha", "border-alpha", "text-alpha"], "interactive"=>["clickable", "smooth", "busy", "ghost", "unselectable", "scroll-smooth", "scroll", "cursor-col-resize", "cursor-row-resize", "transition-all", "transition-colors", "transition-none", "ring", "resize", "outline-none"], "internal"=>["qss", "dev-border", "group", "foreign", "qss-legibility-override", "qss-ergonomic-override", "qss-payload-override", "qss-dampen-scaling", "qss-content-driven"]}.freeze
124
+ ADJECTIVE_CATEGORIES = {"positional"=>["detached", "floating", "stuck", "absolute", "pos-x", "pos-y", "pos-r", "pos-b", "pos"], "flow_relative"=>["anchored", "relative"], "layout"=>["vertical", "horizontal", "centered", "v-mid", "h-mid", "v-start", "v-end", "h-start", "h-end", "spaced", "around", "evenly", "flex", "flex-col", "flex-row", "flex-wrap", "flex-nowrap", "flex-1", "flex-auto", "flex-none", "items-center", "justify-center", "justify-between", "grid", "table-fixed", "contain-layout", "w-full", "h-full", "w-f", "h-f"], "typographic"=>["italic", "uppercase", "lowercase", "underline", "nowrap", "truncate", "font-mono", "font-sans", "font-medium", "font-bold", "font-black", "text-size", "leading", "tracking", "tracking-tighter", "tracking-wide", "tracking-wider", "tracking-widest", "icon-size"], "visual"=>["bg", "border", "border-t", "border-b", "border-l", "border-r", "rounded", "rounded-full", "shadow", "opacity", "blur", "bright", "contrast", "grayscale", "hue", "invert", "saturate", "sepia", "blend", "contain", "cover", "visible", "invisible", "bg-alpha", "border-alpha", "text-alpha"], "interactive"=>["clickable", "smooth", "busy", "ghost", "unselectable", "scroll-smooth", "scroll", "cursor-col-resize", "cursor-row-resize", "transition-all", "transition-colors", "transition-none", "ring", "resize", "outline-none"], "internal"=>["qss", "dev-border", "group", "foreign", "qss-legibility-override", "qss-ergonomic-override", "qss-payload-override", "qss-dampen-scaling", "qss-content-driven"]}.freeze
124
125
 
125
126
  ILLEGAL_COMBOS = [{"tag_in"=>"payload_tags", "forbid_category"=>"positional", "severity"=>"red", "message"=>"Protocol Violation: Payload tags (<tag>) cannot carry flow-breaking positional adjectives. Use a wrapping <div> Frame.", "escape_hatch"=>"qss-payload-override"}].freeze
126
127
 
data/lib/qss/version.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  module QSS
4
4
  # The gem's version. QSS::VERSION is the usual spelling; QSS::Version::STRING the same value.
5
5
  module Version
6
- STRING = "0.1.0"
6
+ STRING = "0.1.2"
7
7
  end
8
8
 
9
9
  VERSION = Version::STRING
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: qss
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kraig Fairhurst
@@ -93,10 +93,16 @@ dependencies:
93
93
  - - ">="
94
94
  - !ruby/object:Gem::Version
95
95
  version: '0'
96
- description: QSS sizes and places layout on a 1000-unit canvas grid (1 qnt = canvas
97
- width / 1000, pure CSS), ships compiled and SCSS sources, and audits views for rule
98
- violations. Includes a Rails engine for the asset pipeline; the auditors also run
99
- without Rails.
96
+ description: |-
97
+ AI coding agents struggle with CSS. Asked to move a sidebar 10px, an agent edits a margin, then an order, then a flex-basis, and something else moves. It then reports the change as done while the page is broken. Flex and grid have no single "left" property, so the agent has to guess which one to touch.
98
+
99
+ QSS makes layout addressable. Every structural value is an explicit, single-purpose quantity on a 1000-unit canvas grid (1 qnt = canvas width / 1000, computed in pure CSS), so each edit has one target and the same numbers hold at any screen size. Frames sit flat on one canvas, isolate their layout, and overlays portal to the document root.
100
+
101
+ QSS also audits what can be checked statically. Two auditors flag violations such as !important, inline layout styles, undeclared sizes and raw viewport units. RED marks a rule violation and ORANGE a judgment call. Static checks can't prove a page renders, so QSS includes a render check for the compiled CSS alone, and its design history is explicit that only a real browser is real proof.
102
+
103
+ QSS was extracted from Parapet, a Rails development tool, which was its first consumer. Its early custom parser was a bug farm, and an invented syntax costs agents more than it buys. Structure moved to plain CSS custom properties; bracket values remain for content such as spacing and sizes. What was tried and dropped is in docs/DESIGN_HISTORY.md.
104
+
105
+ Status: 0.1.x. Expect names and APIs to change before 1.0.
100
106
  email:
101
107
  - kmfairhurst@gmail.com
102
108
  executables:
@@ -132,6 +138,7 @@ metadata:
132
138
  source_code_uri: https://bitbucket.org/kmf2/qss
133
139
  bug_tracker_uri: https://bitbucket.org/kmf2/qss/issues
134
140
  changelog_uri: https://bitbucket.org/kmf2/qss/src/main/CHANGELOG.md
141
+ documentation_uri: https://bitbucket.org/kmf2/qss/src/main/docs/DESIGN_HISTORY.md
135
142
  allowed_push_host: https://rubygems.org
136
143
  rubygems_mfa_required: 'true'
137
144
  rdoc_options: []
@@ -150,5 +157,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
150
157
  requirements: []
151
158
  rubygems_version: 4.0.13
152
159
  specification_version: 4
153
- summary: 'Quantized Spatial Styling: an agent-aware CSS layout framework, with auditors'
160
+ summary: 'Quantized Spatial Styling: CSS layout that AI agents can read, edit and
161
+ check from source'
154
162
  test_files: []