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 +4 -4
- data/CHANGELOG.md +19 -1
- data/README.md +1 -1
- data/app/assets/javascripts/qss/qss-engine.js +1 -0
- data/app/assets/stylesheets/qss/qss_core.css +10 -0
- data/app/assets/stylesheets/qss/qss_core.scss +5 -0
- data/config/grammar.yml +1 -0
- data/exe/qss-core-render-check +5 -0
- data/lib/qss/qss_auditor.rb +2 -1
- data/lib/qss/version.rb +1 -1
- metadata +14 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8ede42a34241c427ed061678140580661f4dbb494ded598e312e64e34fdcf211
|
|
4
|
+
data.tar.gz: 813b25c7cf09e631881a0c410d3625dd43f556aa709c165be4fdc33a0d2ce73d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
## [
|
|
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.
|
|
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
|
|
|
@@ -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
data/exe/qss-core-render-check
CHANGED
|
@@ -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
|
|
data/lib/qss/qss_auditor.rb
CHANGED
|
@@ -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
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.
|
|
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:
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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:
|
|
160
|
+
summary: 'Quantized Spatial Styling: CSS layout that AI agents can read, edit and
|
|
161
|
+
check from source'
|
|
154
162
|
test_files: []
|