@profitlich/template-toolkit 2.4.1 → 2.4.3
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.
- package/dev/toolbar/COLUMNS.md +228 -0
- package/package.json +1 -1
- package/scss/core/layout.scss +1 -3
- package/scss/core/mediaqueries.scss +1 -1
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# Plan: Exakte Spaltenlinien via Canvas (Variante E)
|
|
2
|
+
|
|
3
|
+
## Context
|
|
4
|
+
|
|
5
|
+
Im `lines`-Modus des Dev-Grids werden Spaltenlinien über `linear-gradient` + `background-size` als Kachelmuster erzeugt. Die Kachelbreite löst auf Bruchpixel-Werte auf → die `0.5px`-Gradient-Stops landen an leicht unterschiedlichen physischen Pixeln pro Kachel. Ergebnis: Linien erscheinen wackelnd oder inkonsistent.
|
|
6
|
+
|
|
7
|
+
**Lösung:** `<canvas>`-Element mit korrekter `devicePixelRatio`-Behandlung und dem "Half-Pixel"-Trick für pixelgenaue, scharf gerenderte Linien. Funktioniert auf 1×- und Retina-Displays gleich gut.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Betroffene Dateien
|
|
12
|
+
|
|
13
|
+
- [dev/toolbar/Toolbar.js](dev/toolbar/Toolbar.js)
|
|
14
|
+
- [dev/toolbar/toolbar.scss](dev/toolbar/toolbar.scss)
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Architektur
|
|
19
|
+
|
|
20
|
+
```text
|
|
21
|
+
.dev-toolbar__grid (position: fixed, 100vw×100vh)
|
|
22
|
+
└── <canvas> ← neu, ersetzt ::after für "lines"
|
|
23
|
+
::after ← bleibt für "ribbons" erhalten
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Das Canvas-Element wird als Kind von `#gridElement` angehängt. Es erbt dessen Fixed-Positioning und Visibility-Steuerung über `data-dev`. Für den `lines`-Modus zeichnet das Canvas; für `ribbons` wird das Canvas geleert und `::after` übernimmt.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## SCSS — `toolbar.scss`
|
|
31
|
+
|
|
32
|
+
### 1. CSS Custom Properties im Mixin ergänzen
|
|
33
|
+
|
|
34
|
+
Im Block `body[data-dev='true']` innerhalb von `dev-toolbar-grid`:
|
|
35
|
+
|
|
36
|
+
```scss
|
|
37
|
+
body[data-dev='true'] {
|
|
38
|
+
--dev-columns: #{$columns}; // unitless integer
|
|
39
|
+
--dev-gutter: #{$gutter}; // CSS-Wert (z. B. "1.5rem" oder "20px")
|
|
40
|
+
--dev-margin-left: #{$margin-left};
|
|
41
|
+
--dev-margin-right: #{$margin-right};
|
|
42
|
+
|
|
43
|
+
.dev-toolbar__grid::after { ... } // unverändert
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 2. `lines`-Gradient entfernen
|
|
48
|
+
|
|
49
|
+
Den gesamten Block (Zeilen 28–39) entfernen — das Canvas übernimmt die Darstellung:
|
|
50
|
+
|
|
51
|
+
```scss
|
|
52
|
+
// ENTFERNEN:
|
|
53
|
+
body[data-dev='true'][data-dev-grid="lines"] {
|
|
54
|
+
.dev-toolbar__grid::after { background: ...; }
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 3. Canvas-Styling ergänzen
|
|
59
|
+
|
|
60
|
+
```scss
|
|
61
|
+
.dev-toolbar__grid {
|
|
62
|
+
canvas {
|
|
63
|
+
display: block;
|
|
64
|
+
width: 100%;
|
|
65
|
+
height: 100%;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## JavaScript — `Toolbar.js`
|
|
73
|
+
|
|
74
|
+
### Neue private Fields
|
|
75
|
+
|
|
76
|
+
```javascript
|
|
77
|
+
#gridElement = null; // bisher nur lokale Variable, jetzt gespeichert
|
|
78
|
+
#canvas = null;
|
|
79
|
+
#ctx = null;
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### `constructor()` — Änderungen
|
|
83
|
+
|
|
84
|
+
1. `gridOverlay` → `this.#gridElement` (Referenz speichern)
|
|
85
|
+
2. `#initCanvas()` aufrufen nach `prepend`
|
|
86
|
+
|
|
87
|
+
```javascript
|
|
88
|
+
this.#gridElement = document.createElement('div');
|
|
89
|
+
this.#gridElement.classList.add('dev-toolbar__grid');
|
|
90
|
+
document.body.prepend(this.#gridElement);
|
|
91
|
+
this.#initCanvas();
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Neue Methode `#initCanvas()`
|
|
95
|
+
|
|
96
|
+
```javascript
|
|
97
|
+
#initCanvas() {
|
|
98
|
+
this.#canvas = document.createElement('canvas');
|
|
99
|
+
this.#gridElement.appendChild(this.#canvas);
|
|
100
|
+
this.#ctx = this.#canvas.getContext('2d');
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Neue Methode `#drawGrid()`
|
|
105
|
+
|
|
106
|
+
Wird aufgerufen bei: State-Wechsel (`#applyState`), Resize (`#onResize`).
|
|
107
|
+
|
|
108
|
+
```javascript
|
|
109
|
+
#drawGrid() {
|
|
110
|
+
const dpr = window.devicePixelRatio || 1;
|
|
111
|
+
const w = window.innerWidth;
|
|
112
|
+
const h = window.innerHeight;
|
|
113
|
+
|
|
114
|
+
this.#canvas.width = Math.round(w * dpr);
|
|
115
|
+
this.#canvas.height = Math.round(h * dpr);
|
|
116
|
+
|
|
117
|
+
this.#ctx.clearRect(0, 0, this.#canvas.width, this.#canvas.height);
|
|
118
|
+
|
|
119
|
+
if (this.#state.grid !== 'lines') return;
|
|
120
|
+
|
|
121
|
+
this.#ctx.setTransform(dpr, 0, 0, dpr, 0.5, 0); // scale + half-pixel shift
|
|
122
|
+
|
|
123
|
+
this.#drawColumnLines();
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**Half-pixel-Trick:** `ctx.setTransform(dpr, 0, 0, dpr, 0.5, 0)` — der 0.5px-Shift zentriert 1px-Linien exakt auf physische Pixel bei dpr=1. Bei dpr=2 (Retina) ist 0.5 CSS-px = 1 physischer Pixel, ebenfalls scharf.
|
|
128
|
+
|
|
129
|
+
### Neue Methode `#drawColumnLines()`
|
|
130
|
+
|
|
131
|
+
```javascript
|
|
132
|
+
#drawColumnLines() {
|
|
133
|
+
const style = getComputedStyle(document.body);
|
|
134
|
+
const columns = parseInt(style.getPropertyValue('--dev-columns'));
|
|
135
|
+
const gutter = this.#resolveToPx(style.getPropertyValue('--dev-gutter').trim());
|
|
136
|
+
const marginLeft = this.#resolveToPx(style.getPropertyValue('--dev-margin-left').trim());
|
|
137
|
+
|
|
138
|
+
// Kachelbreite = (Viewport - Margins + Gutter) / Spalten
|
|
139
|
+
// (identisch zur background-size-Formel im SCSS)
|
|
140
|
+
const gridWidth = window.innerWidth - marginLeft
|
|
141
|
+
- this.#resolveToPx(style.getPropertyValue('--dev-margin-right').trim())
|
|
142
|
+
+ gutter;
|
|
143
|
+
const tileWidth = gridWidth / columns;
|
|
144
|
+
const colContent = tileWidth - gutter;
|
|
145
|
+
|
|
146
|
+
this.#ctx.strokeStyle = 'rgba(0, 0, 255, 0.5)';
|
|
147
|
+
this.#ctx.lineWidth = 1;
|
|
148
|
+
const h = window.innerHeight;
|
|
149
|
+
|
|
150
|
+
for (let i = 0; i < columns; i++) {
|
|
151
|
+
const leftEdge = marginLeft + i * tileWidth;
|
|
152
|
+
const rightEdge = leftEdge + colContent;
|
|
153
|
+
|
|
154
|
+
this.#ctx.beginPath();
|
|
155
|
+
this.#ctx.moveTo(Math.round(leftEdge), 0);
|
|
156
|
+
this.#ctx.lineTo(Math.round(leftEdge), h);
|
|
157
|
+
this.#ctx.stroke();
|
|
158
|
+
|
|
159
|
+
this.#ctx.beginPath();
|
|
160
|
+
this.#ctx.moveTo(Math.round(rightEdge), 0);
|
|
161
|
+
this.#ctx.lineTo(Math.round(rightEdge), h);
|
|
162
|
+
this.#ctx.stroke();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Neue Methode `#resolveToPx(cssValue)`
|
|
168
|
+
|
|
169
|
+
Löst beliebige CSS-Einheiten (rem, vw, calc, …) auf px auf, ohne Unit-Parsing-Logik:
|
|
170
|
+
|
|
171
|
+
```javascript
|
|
172
|
+
#resolveToPx(cssValue) {
|
|
173
|
+
if (/^-?\d+(\.\d+)?px$/.test(cssValue)) return parseFloat(cssValue);
|
|
174
|
+
|
|
175
|
+
const probe = document.createElement('div');
|
|
176
|
+
probe.style.cssText = `position:fixed;visibility:hidden;width:${cssValue};top:0;left:0;`;
|
|
177
|
+
document.body.appendChild(probe);
|
|
178
|
+
const px = probe.getBoundingClientRect().width;
|
|
179
|
+
probe.remove();
|
|
180
|
+
return px;
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### `#applyState()` — Ergänzung
|
|
185
|
+
|
|
186
|
+
```javascript
|
|
187
|
+
#applyState() {
|
|
188
|
+
const gridActive = this.#state.grid !== 'aus';
|
|
189
|
+
document.body.setAttribute('data-dev', String(gridActive));
|
|
190
|
+
document.body.setAttribute('data-dev-grid', this.#state.grid);
|
|
191
|
+
this.#updateImageSize();
|
|
192
|
+
this.#drawGrid(); // ← neu
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### `#onResize` — Ergänzung
|
|
197
|
+
|
|
198
|
+
```javascript
|
|
199
|
+
#onResize = () => {
|
|
200
|
+
this.#gui.title(this.#getViewportText());
|
|
201
|
+
this.#updateImageSize();
|
|
202
|
+
this.#drawGrid(); // ← neu
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Positionsformel (Herleitung)
|
|
209
|
+
|
|
210
|
+
Das SCSS positioniert `::after` mit `margin-left = marginLeft - gutter/2`, sodass die Kacheln mittig zwischen Spalten beginnen. Die resultierenden Canvas-Positionen sind:
|
|
211
|
+
|
|
212
|
+
| | Formel | Beispiel (12 Spalten, gutter=20px, marginLeft=40px) |
|
|
213
|
+
| --- | --- | --- |
|
|
214
|
+
| Linke Kante Spalte i | `marginLeft + i × tileWidth` | i=0: 40px, i=1: 120px, … |
|
|
215
|
+
| Rechte Kante Spalte i | `marginLeft + i × tileWidth + colContent` | i=0: 100px, i=1: 180px, … |
|
|
216
|
+
|
|
217
|
+
Diese Formel ist identisch zur bestehenden `background-size`-Logik, nur ohne Kachel-Grenz-Problem.
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Verifikation
|
|
222
|
+
|
|
223
|
+
1. `ddev npm run dev` starten
|
|
224
|
+
2. Dev-Toolbar öffnen (Ctrl), Grid auf "lines" schalten
|
|
225
|
+
3. **Browserfenster in verschiedene Breiten ziehen** → Linien dürfen nicht wackeln
|
|
226
|
+
4. **Safari (Retina) + Chrome (1×)** vergleichen → gleiche Schärfe
|
|
227
|
+
5. **Ribbons-Modus** prüfen → Canvas geleert, `::after`-Gradient unverändert
|
|
228
|
+
6. **Breakpoint-Wechsel** prüfen → CSS Custom Properties werden neu gelesen, Linienanzahl passt sich an
|
package/package.json
CHANGED
package/scss/core/layout.scss
CHANGED
|
@@ -197,8 +197,6 @@ $mediaqueries: (
|
|
|
197
197
|
// Distance
|
|
198
198
|
@mixin marginPadding($layout, $mode, $position, $top: 0, $right: 0, $bottom: 0, $left: 0, $important: null) {
|
|
199
199
|
|
|
200
|
-
// @include mode($layout, padding/margin, all/top/right/bottom/left, 0, 0, 0, 0)
|
|
201
|
-
|
|
202
200
|
@if $important == true {
|
|
203
201
|
$important: '!important';
|
|
204
202
|
}
|
|
@@ -225,7 +223,7 @@ $mediaqueries: (
|
|
|
225
223
|
}
|
|
226
224
|
|
|
227
225
|
@if $position == all {
|
|
228
|
-
#{$mode}: $top $right $bottom $left $important;
|
|
226
|
+
#{$mode}: $top $right $bottom $left #{$important};
|
|
229
227
|
}
|
|
230
228
|
@if $position == top {
|
|
231
229
|
#{$mode}-top: $top;
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// Wenn es keine $mediaquery in der map gibt, gebe einen Fehler aus
|
|
14
14
|
@if not $queries {
|
|
15
15
|
@error "No value could be retrieved from `#{$mediaquery}`. "
|
|
16
|
-
+"Please make sure it is defined in `$mediaqueries` map.";
|
|
16
|
+
+ "Please make sure it is defined in `$mediaqueries` map.";
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
// Include the media mixin with $queries
|