qss 0.1.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 +7 -0
- data/CHANGELOG.md +24 -0
- data/CONTRIBUTING.md +26 -0
- data/MIT-LICENSE +20 -0
- data/QSS-MANIFESTO.md +61 -0
- data/README.md +159 -0
- data/app/assets/javascripts/qss/qss-engine.js +1054 -0
- data/app/assets/stylesheets/qss/_qss_defaults.scss +74 -0
- data/app/assets/stylesheets/qss/qss_core.css +205042 -0
- data/app/assets/stylesheets/qss/qss_core.scss +614 -0
- data/config/grammar.yml +265 -0
- data/config/qss-objects.json +3 -0
- data/exe/qss-core-render-check +104 -0
- data/exe/qss-sync +205 -0
- data/lib/qss/blueprints.rb +106 -0
- data/lib/qss/configuration.rb +82 -0
- data/lib/qss/engine.rb +9 -0
- data/lib/qss/qss_auditor.rb +412 -0
- data/lib/qss/qss_native_auditor.rb +908 -0
- data/lib/qss/version.rb +10 -0
- data/lib/qss.rb +26 -0
- metadata +154 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: a8e93da5bd3a65eb8804f5416a1a0315f23110bb6027703c71974b7fa4443d73
|
|
4
|
+
data.tar.gz: d00e38e05740739f64aff39d17f491558f76a0556ec71abaa7781c278059c60c
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: dc74ce934ef4007d538d184b46822b89bbb3e6ee1c0a2ff90900ae69729434e5413dac0380c33976ae2ba43cf01a9a30e6b67140f4055fdadf758781b31f7be1
|
|
7
|
+
data.tar.gz: aa18e0742c0dfc5bb26067fed5dff96f52b28f0e495634b9aac87e1ead4ed60ae5124cae80b12f5b4fce43f44f2a06f7d8f71f2f7c8e99f0c68b3147f4799931
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the qss gem. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/);
|
|
4
|
+
versions follow [Semantic Versioning](https://semver.org/) (before 1.0, minor versions may break the API).
|
|
5
|
+
|
|
6
|
+
## [0.1.0] - unreleased
|
|
7
|
+
|
|
8
|
+
First release, extracted from the Parapet project.
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Core stylesheet (`qss/qss_core.scss`, compiled `qss/qss_core.css`): utilities on a 1000-unit canvas grid; the unit
|
|
12
|
+
(`--qnt`, canvas width / 1000) computed by CSS alone (`0.1cqw` of the canvas; the viewport width outside one).
|
|
13
|
+
- Base reset for QSS actors at the canvas id's weight only, so every core utility applies without the JS engine.
|
|
14
|
+
- Neutral `!default` SCSS settings (`qss/_qss_defaults.scss`); a project sets its own before importing core.
|
|
15
|
+
- Optional runtime engine (`qss/qss-engine.js`): rules for uncovered bracket classes, measured element edges.
|
|
16
|
+
- `QSS::QssNativeAuditor`: id grammar (`{qualifier}-{baseObject}`, `QSSObject` custom types) with a per-project
|
|
17
|
+
registry, `qss-function` / `qss-relation` attributes, explicit dimensions for every actor, unit rules (no raw CSS
|
|
18
|
+
lengths on QSS structure; rem or % only in foreign content), `!important`, z-index and inline-style checks.
|
|
19
|
+
- `QSS::QssAuditor`: the older class-whitelist auditor.
|
|
20
|
+
- `QSS.configure(root:, config:)`: a project's settings read at runtime; paths resolve against its root.
|
|
21
|
+
- `QSS::Blueprints` (`QSS.grid`, `QSS.object`): layout arithmetic and object blueprints.
|
|
22
|
+
- `QSS::Engine`: Rails engine for the asset pipeline, loaded only when Rails is present.
|
|
23
|
+
- `qss-sync`: writes a project's SCSS settings (`--core` regenerates the gem's own generated sections).
|
|
24
|
+
- `qss-core-render-check`: renders the compiled CSS alone in headless Chrome and checks core utilities.
|
data/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Contributing to QSS
|
|
2
|
+
|
|
3
|
+
Thank you for helping. Issues and pull requests live at https://bitbucket.org/kmf2/qss.
|
|
4
|
+
|
|
5
|
+
## Reporting a bug
|
|
6
|
+
|
|
7
|
+
Please include the QSS version, Ruby (and Rails) version, the smallest markup that shows the problem, and what the
|
|
8
|
+
auditor or browser did versus what you expected.
|
|
9
|
+
|
|
10
|
+
## Changing code
|
|
11
|
+
|
|
12
|
+
1. `bundle install`
|
|
13
|
+
2. Make the change, with a spec for it under `spec/`.
|
|
14
|
+
3. Run `bundle exec rake` (RuboCop, then the render check) and `bundle exec rspec`; all must pass.
|
|
15
|
+
4. If you changed `config/grammar.yml`, run `bundle exec rake sync` and commit the regenerated sections. Never edit
|
|
16
|
+
between the `QSS-GENERATED-BEGIN` / `QSS-GENERATED-END` markers by hand.
|
|
17
|
+
5. If you changed `app/assets/stylesheets/qss/qss_core.scss`, run `bundle exec rake compile` and commit
|
|
18
|
+
`qss_core.css`.
|
|
19
|
+
6. Add a line to the Unreleased section of `CHANGELOG.md`.
|
|
20
|
+
|
|
21
|
+
QSS keeps layout readable from source: a change that makes a layout depend on rendering, on JavaScript, or on a
|
|
22
|
+
second coordinate system needs a strong reason, stated in the pull request.
|
|
23
|
+
|
|
24
|
+
## License
|
|
25
|
+
|
|
26
|
+
By contributing, you agree that your contributions are licensed under the MIT License.
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright Kraig Fairhurst
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/QSS-MANIFESTO.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# QSS: Quantized Spatial Styling Manifesto
|
|
2
|
+
## Version 1.2 — "Semantic Object Architecture"
|
|
3
|
+
|
|
4
|
+
### 1. The Core Philosophy
|
|
5
|
+
QSS is NOT a CSS framework. It is a **Spatial Operating System** for the browser.
|
|
6
|
+
|
|
7
|
+
Traditional CSS treats the web as a "Document Stream" where elements flow like ink on paper. This is inherently non-deterministic and difficult for AI Agents to reason about without querying the computed DOM.
|
|
8
|
+
|
|
9
|
+
**QSS rejects flow.** It treats the screen as a 1000-unit 2D CAD canvas. Every element has a mathematical origin and a predictable footprint.
|
|
10
|
+
|
|
11
|
+
### 2. The Deterministic Stack
|
|
12
|
+
To ensure isolation and predictable rendering, QSS requires the **Safety Trio**:
|
|
13
|
+
|
|
14
|
+
1. **Stage (`#qss-canvas`)**: The relative-positioned root. All spatial math is relative to this origin.
|
|
15
|
+
2. **Wrapper (`#qss-wrapper`)**: Enforces viewport boundaries and provides a "Safety Gutter."
|
|
16
|
+
3. **Noun (`id="..."`)**: The unique Identity. Every "Object" on the canvas is a Noun defined by its ID.
|
|
17
|
+
4. **Actor (`.qss`)**: The mandatory base class (The DNA). It enforces `position: absolute` and a "Nuclear Reset."
|
|
18
|
+
|
|
19
|
+
### 3. Grammar: The "Adjective" System
|
|
20
|
+
Adjectives define the traits, positioning, and state of a Noun.
|
|
21
|
+
|
|
22
|
+
1. **Atomic Adjectives** (Classes): `.[prop]\[[val]]` pattern (e.g., `.qss-pos-x[500]`).
|
|
23
|
+
2. **Parametric Adjectives** (Composite Classes): `.[NounType]\[[p1]|[p2]|...]` pattern.
|
|
24
|
+
|
|
25
|
+
### 4. Semantic Object Registry & Blueprints
|
|
26
|
+
Version 1.2 introduces **Parametric Blueprints**. While the `id` is the Noun (Identity), the blueprint class (e.g., `.qss-Button`) is a **Composite Adjective** that applies a pre-defined set of traits from `qss-objects.json`.
|
|
27
|
+
|
|
28
|
+
- **Intrinsic Defaults**: Using `.qss-Button` applies the `intrinsic` dimensions defined in the registry.
|
|
29
|
+
- **Parametric Overrides**: Using `.qss-Button[200|80]` overrides specific dimensions (Width | Height) JIT.
|
|
30
|
+
|
|
31
|
+
#### Example: Parametric Dropdown Blueprint
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"name": "Dropdown",
|
|
35
|
+
"params": ["w", "h", "row_height"],
|
|
36
|
+
"intrinsic": { "w": 200, "h": 40 },
|
|
37
|
+
"slots": [
|
|
38
|
+
{ "name": "header", "pos": [0, 0] },
|
|
39
|
+
{ "name": "list", "pos": [0, 42], "h": "computed" }
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 5. Deterministic Helpers (Ruby/JS)
|
|
45
|
+
QSS provides a **Grid Generator** that allows Agents to "stamp" complex layouts without visual checks.
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
# Ruby instantiation
|
|
49
|
+
<% qss.grid(31, cols: 7, w: 100, h: 100).each do |day| %>
|
|
50
|
+
<div class="qss qss-pos-x[<%= day.x %>] qss-pos-y[<%= day.y %>]">...</div>
|
|
51
|
+
<% end %>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 6. Installation
|
|
55
|
+
1. Copy `qss_core.scss` and `_qss_variables.scss` to your stylesheets.
|
|
56
|
+
2. Include `qss-engine.js` for client-side transpilation.
|
|
57
|
+
3. Configure `qss.config.json` with your project palette.
|
|
58
|
+
4. (Optional) Copy `qss.rb` to your `lib/` for server-side spatial math.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
*Mathematically Audited. Agent Aware. Deterministic by Design.*
|
data/README.md
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# QSS: Quantized Spatial Styling
|
|
2
|
+
|
|
3
|
+
QSS is a CSS layout framework built so that both people and AI agents can read a layout from its source, without
|
|
4
|
+
rendering it. Layout is placed and sized on a 1000-unit grid across a **canvas** element: one **qnt** is one
|
|
5
|
+
thousandth of the canvas width, computed by CSS alone. QSS ships compiled CSS, its SCSS sources, an optional
|
|
6
|
+
JavaScript engine, and two **auditors** that check views against QSS's rules. It includes a Rails engine for the
|
|
7
|
+
asset pipeline; the auditors and configuration also work in plain Ruby.
|
|
8
|
+
|
|
9
|
+
> **Status: 0.1.0, early.** QSS was extracted from the Parapet project (a Rails development tool),
|
|
10
|
+
> which is its first consumer. Expect names and APIs to change before 1.0. See [CHANGELOG.md](CHANGELOG.md).
|
|
11
|
+
> Why QSS is designed this way: [docs/DESIGN_HISTORY.md](docs/DESIGN_HISTORY.md).
|
|
12
|
+
|
|
13
|
+
## Vocabulary
|
|
14
|
+
|
|
15
|
+
- **Canvas**: the element QSS measures against, `<div id="qss-canvas">` by default.
|
|
16
|
+
- **Actor**: an element with class `qss` inside the canvas. Actors declare their size; QSS's reset keeps them from
|
|
17
|
+
shrinking or growing implicitly.
|
|
18
|
+
- **qnt**: the QSS unit, `canvas width / 1000`. `w[240]` means 240 qnt wide: 24% of the canvas, at any screen size.
|
|
19
|
+
- **Noun, adjective, attribute**: an element's `id` is its noun (its identity), its classes are adjectives (its
|
|
20
|
+
traits), and `qss-function` / `qss-relation` are QSS attributes. See [QSS-MANIFESTO.md](QSS-MANIFESTO.md).
|
|
21
|
+
- **Foreign content**: anything inside class `foreign` (or `qss-payload-override`) is left to ordinary CSS.
|
|
22
|
+
|
|
23
|
+
## Installation (Rails)
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
# Gemfile
|
|
27
|
+
gem "qss"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The gem's Rails engine puts QSS's assets on your asset pipeline (`qss/qss_core.css`, `qss/qss_core.scss`,
|
|
31
|
+
`qss/qss-engine.js`).
|
|
32
|
+
|
|
33
|
+
### Markup
|
|
34
|
+
|
|
35
|
+
An id is `{qualifier}-{baseObject}`: the base object is an HTML tag, the qualifier says which one. A qualifier is
|
|
36
|
+
declared once in your registry, recording what it adds to its base. Structural frames (`nav`, `main`, ...) carry
|
|
37
|
+
`contain-layout`.
|
|
38
|
+
|
|
39
|
+
```erb
|
|
40
|
+
<div id="qss-canvas">
|
|
41
|
+
<nav id="main-nav" class="qss anchored contain-layout w[200] h-full"> ... </nav>
|
|
42
|
+
<section id="content-section" class="qss anchored contain-layout w[800] h-full"> ... </section>
|
|
43
|
+
</div>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
```yaml
|
|
47
|
+
# config/qss_registry.yml (auditor.registry_path in your config, below)
|
|
48
|
+
types:
|
|
49
|
+
main-nav:
|
|
50
|
+
extends: nav
|
|
51
|
+
content-section:
|
|
52
|
+
extends: section
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
This example audits green with `QSS::QssNativeAuditor` (below).
|
|
56
|
+
|
|
57
|
+
### Stylesheet: neutral defaults
|
|
58
|
+
|
|
59
|
+
```erb
|
|
60
|
+
<%= stylesheet_link_tag "qss/qss_core" %>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Stylesheet: with your own settings
|
|
64
|
+
|
|
65
|
+
Set your values first, then import core; core's settings are `!default`, so yours win. Put
|
|
66
|
+
`QSS.stylesheets_path` on your Sass load path so `qss/qss_core` resolves:
|
|
67
|
+
|
|
68
|
+
```scss
|
|
69
|
+
// app/assets/stylesheets/application.scss
|
|
70
|
+
@import "qss_variables"; // generated from your config by qss-sync (below)
|
|
71
|
+
@import "qss/qss_core";
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
```ruby
|
|
75
|
+
Sass.compile("app/assets/stylesheets/application.scss",
|
|
76
|
+
load_paths: ["app/assets/stylesheets", QSS.stylesheets_path])
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### The JavaScript engine (optional)
|
|
80
|
+
|
|
81
|
+
The compiled CSS works on its own. The engine adds rules at runtime for bracket classes the compiled CSS does not
|
|
82
|
+
cover, and exports measured element edges as CSS variables:
|
|
83
|
+
|
|
84
|
+
```erb
|
|
85
|
+
<%= javascript_include_tag "qss/qss-engine", defer: true %>
|
|
86
|
+
<script>
|
|
87
|
+
document.addEventListener("DOMContentLoaded", () => { QSSEngine.startPulse(); QSSEngine.startObserver(); });
|
|
88
|
+
</script>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Configuration
|
|
92
|
+
|
|
93
|
+
QSS starts neutral. A project declares its own settings in one YAML file and tells QSS where it is:
|
|
94
|
+
|
|
95
|
+
```ruby
|
|
96
|
+
# config/initializers/qss.rb
|
|
97
|
+
QSS.configure(root: Rails.root, config: "config/qss_config.yml")
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
```yaml
|
|
101
|
+
# config/qss_config.yml -- every key is optional; see the gem's config/grammar.yml for the defaults
|
|
102
|
+
config:
|
|
103
|
+
portal_root: "#overlay-root" # overlays render here; "" = rules scoped to the canvas only
|
|
104
|
+
palette:
|
|
105
|
+
brand-main: "#0f1115"
|
|
106
|
+
group_names: [menu] # named groups for group-*:hover style classes
|
|
107
|
+
auditor:
|
|
108
|
+
id_prefix: "app-" # optional project id prefix
|
|
109
|
+
registry_path: "config/qss_registry.yml"
|
|
110
|
+
sync_targets:
|
|
111
|
+
variables: app/assets/stylesheets/_qss_variables.scss
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Relative paths resolve against the declared `root`, never the working directory.
|
|
115
|
+
|
|
116
|
+
### Generating your SCSS settings
|
|
117
|
+
|
|
118
|
+
```sh
|
|
119
|
+
QSS_CONSUMER_CONFIG=config/qss_config.yml bundle exec qss-sync
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
This writes your `_qss_variables.scss` (the `sync_targets.variables` path). Without `QSS_CONSUMER_CONFIG` it reads
|
|
123
|
+
`config/qss_config.yml` from the current directory.
|
|
124
|
+
|
|
125
|
+
## Auditing views
|
|
126
|
+
|
|
127
|
+
```ruby
|
|
128
|
+
result = QSS::QssNativeAuditor.audit_file("app/views/layouts/application.html.erb")
|
|
129
|
+
result[:status] # => "green", "orange" or "red"
|
|
130
|
+
result[:issues] # => [{ type:, message:, severity: }, ...] (plus code: on most identity and unit findings)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
RED means a rule is violated: fix the markup, or reconsider the rule. ORANGE is a judgment call. Examples of rules:
|
|
134
|
+
every actor declares its width and height (or opts out with `qss-content-driven`); structure carries no raw CSS
|
|
135
|
+
lengths (sizes are in qnt or canvas percentages); no `!important`; ids follow the `{qualifier}-{baseObject}`
|
|
136
|
+
grammar. `QSS::QssAuditor` is the older class-whitelist auditor: it predates the bracket vocabulary (it reports
|
|
137
|
+
`w[200]` as unknown) and is kept for projects that still use its whitelist.
|
|
138
|
+
|
|
139
|
+
## Requirements
|
|
140
|
+
|
|
141
|
+
- Ruby 3.2 or newer. Rails is optional (needed only for the asset-pipeline engine).
|
|
142
|
+
- A current browser with CSS container query units (`cqw`), `@property` and `:where()`.
|
|
143
|
+
|
|
144
|
+
## Development
|
|
145
|
+
|
|
146
|
+
```sh
|
|
147
|
+
bundle install
|
|
148
|
+
bundle exec rspec # the test suite (plain Ruby)
|
|
149
|
+
bundle exec rake rubocop # lint
|
|
150
|
+
bundle exec rake compile # rebuild app/assets/stylesheets/qss/qss_core.css
|
|
151
|
+
bundle exec rake sync # regenerate the generated sections from config/grammar.yml
|
|
152
|
+
bundle exec rake render_check # render qss_core.css alone in headless Chrome (needs selenium-webdriver)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
156
|
+
|
|
157
|
+
## License
|
|
158
|
+
|
|
159
|
+
MIT. See [MIT-LICENSE](MIT-LICENSE).
|