@autumnsgrove/gossamer 0.0.1 → 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.
- package/dist/animation.d.ts +80 -0
- package/dist/animation.d.ts.map +1 -0
- package/dist/characters.d.ts +49 -0
- package/dist/characters.d.ts.map +1 -0
- package/dist/index.d.ts +37 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1284 -2
- package/dist/index.js.map +1 -1
- package/dist/patterns.d.ts +100 -0
- package/dist/patterns.d.ts.map +1 -0
- package/dist/renderer.d.ts +113 -0
- package/dist/renderer.d.ts.map +1 -0
- package/dist/style.css +124 -0
- package/dist/svelte/GossamerBorder.svelte.d.ts +1 -0
- package/dist/svelte/GossamerClouds.svelte.d.ts +1 -0
- package/dist/svelte/GossamerImage.svelte.d.ts +1 -0
- package/dist/svelte/GossamerOverlay.svelte.d.ts +1 -0
- package/dist/svelte/GossamerText.svelte.d.ts +1 -0
- package/dist/svelte/index.d.ts +20 -0
- package/dist/svelte/index.d.ts.map +1 -0
- package/dist/svelte/index.js +3651 -0
- package/dist/svelte/index.js.map +1 -0
- package/dist/svelte/presets.d.ts +38 -0
- package/dist/svelte/presets.d.ts.map +1 -0
- package/dist/utils/canvas.d.ts +73 -0
- package/dist/utils/canvas.d.ts.map +1 -0
- package/dist/utils/image.d.ts +74 -0
- package/dist/utils/image.d.ts.map +1 -0
- package/dist/utils/performance.d.ts +86 -0
- package/dist/utils/performance.d.ts.map +1 -0
- package/package.json +23 -5
- package/src/animation.test.ts +254 -0
- package/src/animation.ts +243 -0
- package/src/characters.test.ts +148 -0
- package/src/characters.ts +164 -0
- package/src/index.test.ts +115 -0
- package/src/index.ts +133 -11
- package/src/patterns.test.ts +273 -0
- package/src/patterns.ts +316 -0
- package/src/renderer.ts +309 -0
- package/src/svelte/GossamerBorder.svelte +326 -0
- package/src/svelte/GossamerClouds.svelte +269 -0
- package/src/svelte/GossamerImage.svelte +266 -0
- package/src/svelte/GossamerOverlay.svelte +232 -0
- package/src/svelte/GossamerText.svelte +239 -0
- package/src/svelte/index.ts +75 -0
- package/src/svelte/presets.ts +174 -0
- package/src/utils/canvas.ts +210 -0
- package/src/utils/image.ts +275 -0
- package/src/utils/performance.ts +282 -0
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
export interface GossamerImageProps {
|
|
3
|
+
/** Image source URL */
|
|
4
|
+
src: string;
|
|
5
|
+
/** Alt text for accessibility */
|
|
6
|
+
alt: string;
|
|
7
|
+
/** Character set (light to dark) */
|
|
8
|
+
characters?: string;
|
|
9
|
+
/** Cell size for ASCII detail level */
|
|
10
|
+
cellSize?: number;
|
|
11
|
+
/** Single color or 'preserve' to keep image colors */
|
|
12
|
+
color?: string | 'preserve';
|
|
13
|
+
/** Invert brightness mapping */
|
|
14
|
+
invert?: boolean;
|
|
15
|
+
/** Output width in pixels */
|
|
16
|
+
width?: number;
|
|
17
|
+
/** Output height in pixels */
|
|
18
|
+
height?: number;
|
|
19
|
+
/** Show original image on hover */
|
|
20
|
+
showOriginalOnHover?: boolean;
|
|
21
|
+
/** Hover transition duration in ms */
|
|
22
|
+
transitionDuration?: number;
|
|
23
|
+
/** Additional CSS class */
|
|
24
|
+
class?: string;
|
|
25
|
+
}
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<script lang="ts">
|
|
29
|
+
import { onMount } from 'svelte';
|
|
30
|
+
import {
|
|
31
|
+
loadImage,
|
|
32
|
+
imageToPixelData,
|
|
33
|
+
sampleImageCells,
|
|
34
|
+
brightnessToChar,
|
|
35
|
+
invertCharacters,
|
|
36
|
+
CHARACTER_SETS,
|
|
37
|
+
} from '../index';
|
|
38
|
+
|
|
39
|
+
// Props with defaults
|
|
40
|
+
let {
|
|
41
|
+
src,
|
|
42
|
+
alt,
|
|
43
|
+
characters = CHARACTER_SETS.standard.characters,
|
|
44
|
+
cellSize = 8,
|
|
45
|
+
color = '#ffffff',
|
|
46
|
+
invert = false,
|
|
47
|
+
width,
|
|
48
|
+
height,
|
|
49
|
+
showOriginalOnHover = false,
|
|
50
|
+
transitionDuration = 300,
|
|
51
|
+
class: className = '',
|
|
52
|
+
}: GossamerImageProps = $props();
|
|
53
|
+
|
|
54
|
+
// State
|
|
55
|
+
let canvas: HTMLCanvasElement;
|
|
56
|
+
let container: HTMLDivElement;
|
|
57
|
+
let isLoading = true;
|
|
58
|
+
let hasError = false;
|
|
59
|
+
let isHovered = false;
|
|
60
|
+
let loadedImage: HTMLImageElement | null = null;
|
|
61
|
+
let imageWidth = 0;
|
|
62
|
+
let imageHeight = 0;
|
|
63
|
+
|
|
64
|
+
// Effective characters (possibly inverted)
|
|
65
|
+
const effectiveCharacters = $derived(invert ? invertCharacters(characters) : characters);
|
|
66
|
+
|
|
67
|
+
async function loadAndRender(): Promise<void> {
|
|
68
|
+
isLoading = true;
|
|
69
|
+
hasError = false;
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
const img = await loadImage(src, { crossOrigin: 'anonymous' });
|
|
73
|
+
loadedImage = img;
|
|
74
|
+
|
|
75
|
+
// Calculate dimensions
|
|
76
|
+
const naturalWidth = img.naturalWidth;
|
|
77
|
+
const naturalHeight = img.naturalHeight;
|
|
78
|
+
const aspectRatio = naturalWidth / naturalHeight;
|
|
79
|
+
|
|
80
|
+
if (width && height) {
|
|
81
|
+
imageWidth = width;
|
|
82
|
+
imageHeight = height;
|
|
83
|
+
} else if (width) {
|
|
84
|
+
imageWidth = width;
|
|
85
|
+
imageHeight = Math.round(width / aspectRatio);
|
|
86
|
+
} else if (height) {
|
|
87
|
+
imageHeight = height;
|
|
88
|
+
imageWidth = Math.round(height * aspectRatio);
|
|
89
|
+
} else {
|
|
90
|
+
imageWidth = naturalWidth;
|
|
91
|
+
imageHeight = naturalHeight;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
renderASCII();
|
|
95
|
+
isLoading = false;
|
|
96
|
+
} catch {
|
|
97
|
+
hasError = true;
|
|
98
|
+
isLoading = false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function renderASCII(): void {
|
|
103
|
+
if (!canvas || !loadedImage) return;
|
|
104
|
+
|
|
105
|
+
canvas.width = imageWidth;
|
|
106
|
+
canvas.height = imageHeight;
|
|
107
|
+
|
|
108
|
+
const ctx = canvas.getContext('2d');
|
|
109
|
+
if (!ctx) return;
|
|
110
|
+
|
|
111
|
+
// Get image pixel data
|
|
112
|
+
const pixelData = imageToPixelData(loadedImage, imageWidth, imageHeight);
|
|
113
|
+
const cells = sampleImageCells(pixelData, cellSize, cellSize);
|
|
114
|
+
|
|
115
|
+
// Clear canvas
|
|
116
|
+
ctx.clearRect(0, 0, imageWidth, imageHeight);
|
|
117
|
+
|
|
118
|
+
// Set up text rendering
|
|
119
|
+
ctx.font = `${cellSize}px monospace`;
|
|
120
|
+
ctx.textBaseline = 'top';
|
|
121
|
+
ctx.textAlign = 'left';
|
|
122
|
+
|
|
123
|
+
// Render each cell
|
|
124
|
+
for (let row = 0; row < cells.length; row++) {
|
|
125
|
+
for (let col = 0; col < cells[row].length; col++) {
|
|
126
|
+
const cell = cells[row][col];
|
|
127
|
+
const char = brightnessToChar(cell.brightness, effectiveCharacters);
|
|
128
|
+
|
|
129
|
+
if (char !== ' ') {
|
|
130
|
+
// Use image color or fixed color
|
|
131
|
+
ctx.fillStyle = color === 'preserve' ? cell.color : color;
|
|
132
|
+
ctx.fillText(char, col * cellSize, row * cellSize);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Lifecycle
|
|
139
|
+
onMount(() => {
|
|
140
|
+
loadAndRender();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// React to src changes
|
|
144
|
+
$effect(() => {
|
|
145
|
+
if (src) {
|
|
146
|
+
loadAndRender();
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// React to render config changes
|
|
151
|
+
$effect(() => {
|
|
152
|
+
// Track these dependencies
|
|
153
|
+
const _ = [characters, cellSize, color, invert, width, height];
|
|
154
|
+
if (loadedImage) {
|
|
155
|
+
renderASCII();
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
function handleMouseEnter(): void {
|
|
160
|
+
if (showOriginalOnHover) {
|
|
161
|
+
isHovered = true;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function handleMouseLeave(): void {
|
|
166
|
+
isHovered = false;
|
|
167
|
+
}
|
|
168
|
+
</script>
|
|
169
|
+
|
|
170
|
+
<div
|
|
171
|
+
bind:this={container}
|
|
172
|
+
class="gossamer-image {className}"
|
|
173
|
+
class:loading={isLoading}
|
|
174
|
+
class:error={hasError}
|
|
175
|
+
class:hoverable={showOriginalOnHover}
|
|
176
|
+
style:width={imageWidth ? `${imageWidth}px` : undefined}
|
|
177
|
+
style:height={imageHeight ? `${imageHeight}px` : undefined}
|
|
178
|
+
role="img"
|
|
179
|
+
aria-label={alt}
|
|
180
|
+
onmouseenter={handleMouseEnter}
|
|
181
|
+
onmouseleave={handleMouseLeave}
|
|
182
|
+
>
|
|
183
|
+
{#if isLoading}
|
|
184
|
+
<div class="gossamer-image-loading">
|
|
185
|
+
<span>Loading...</span>
|
|
186
|
+
</div>
|
|
187
|
+
{:else if hasError}
|
|
188
|
+
<div class="gossamer-image-error">
|
|
189
|
+
<span>Failed to load image</span>
|
|
190
|
+
</div>
|
|
191
|
+
{:else}
|
|
192
|
+
<canvas
|
|
193
|
+
bind:this={canvas}
|
|
194
|
+
class="gossamer-canvas"
|
|
195
|
+
class:hidden={showOriginalOnHover && isHovered}
|
|
196
|
+
style:transition-duration="{transitionDuration}ms"
|
|
197
|
+
aria-hidden="true"
|
|
198
|
+
></canvas>
|
|
199
|
+
|
|
200
|
+
{#if showOriginalOnHover && loadedImage}
|
|
201
|
+
<img
|
|
202
|
+
{src}
|
|
203
|
+
{alt}
|
|
204
|
+
class="gossamer-original"
|
|
205
|
+
class:visible={isHovered}
|
|
206
|
+
style:transition-duration="{transitionDuration}ms"
|
|
207
|
+
width={imageWidth}
|
|
208
|
+
height={imageHeight}
|
|
209
|
+
/>
|
|
210
|
+
{/if}
|
|
211
|
+
{/if}
|
|
212
|
+
</div>
|
|
213
|
+
|
|
214
|
+
<style>
|
|
215
|
+
.gossamer-image {
|
|
216
|
+
position: relative;
|
|
217
|
+
display: inline-block;
|
|
218
|
+
overflow: hidden;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.gossamer-image.hoverable {
|
|
222
|
+
cursor: pointer;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.gossamer-canvas {
|
|
226
|
+
display: block;
|
|
227
|
+
opacity: 1;
|
|
228
|
+
transition-property: opacity;
|
|
229
|
+
transition-timing-function: ease-in-out;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.gossamer-canvas.hidden {
|
|
233
|
+
opacity: 0;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.gossamer-original {
|
|
237
|
+
position: absolute;
|
|
238
|
+
inset: 0;
|
|
239
|
+
width: 100%;
|
|
240
|
+
height: 100%;
|
|
241
|
+
object-fit: cover;
|
|
242
|
+
opacity: 0;
|
|
243
|
+
transition-property: opacity;
|
|
244
|
+
transition-timing-function: ease-in-out;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.gossamer-original.visible {
|
|
248
|
+
opacity: 1;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.gossamer-image-loading,
|
|
252
|
+
.gossamer-image-error {
|
|
253
|
+
display: flex;
|
|
254
|
+
align-items: center;
|
|
255
|
+
justify-content: center;
|
|
256
|
+
width: 100%;
|
|
257
|
+
height: 100%;
|
|
258
|
+
min-height: 100px;
|
|
259
|
+
color: currentColor;
|
|
260
|
+
opacity: 0.5;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.gossamer-image-error {
|
|
264
|
+
color: #ef4444;
|
|
265
|
+
}
|
|
266
|
+
</style>
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { PatternType } from '../index';
|
|
3
|
+
|
|
4
|
+
export type BlendMode = 'normal' | 'multiply' | 'screen' | 'overlay' | 'soft-light' | 'hard-light' | 'difference';
|
|
5
|
+
|
|
6
|
+
export interface GossamerOverlayProps {
|
|
7
|
+
/** Pattern type */
|
|
8
|
+
pattern?: PatternType;
|
|
9
|
+
/** Character set (light to dark) */
|
|
10
|
+
characters?: string;
|
|
11
|
+
/** Foreground color */
|
|
12
|
+
color?: string;
|
|
13
|
+
/** Overall opacity (0-1) */
|
|
14
|
+
opacity?: number;
|
|
15
|
+
/** CSS blend mode */
|
|
16
|
+
blendMode?: BlendMode;
|
|
17
|
+
/** Enable animation */
|
|
18
|
+
animated?: boolean;
|
|
19
|
+
/** Animation speed */
|
|
20
|
+
speed?: number;
|
|
21
|
+
/** Pattern frequency */
|
|
22
|
+
frequency?: number;
|
|
23
|
+
/** Pattern amplitude */
|
|
24
|
+
amplitude?: number;
|
|
25
|
+
/** Cell size in pixels */
|
|
26
|
+
cellSize?: number;
|
|
27
|
+
/** Target FPS */
|
|
28
|
+
fps?: number;
|
|
29
|
+
/** Additional CSS class */
|
|
30
|
+
class?: string;
|
|
31
|
+
}
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<script lang="ts">
|
|
35
|
+
import { onMount } from 'svelte';
|
|
36
|
+
import {
|
|
37
|
+
GossamerRenderer,
|
|
38
|
+
generateBrightnessGrid,
|
|
39
|
+
createVisibilityObserver,
|
|
40
|
+
createResizeObserver,
|
|
41
|
+
onReducedMotionChange,
|
|
42
|
+
CHARACTER_SETS,
|
|
43
|
+
} from '../index';
|
|
44
|
+
|
|
45
|
+
// Props with defaults
|
|
46
|
+
let {
|
|
47
|
+
pattern = 'perlin',
|
|
48
|
+
characters = CHARACTER_SETS.minimal.characters,
|
|
49
|
+
color = 'currentColor',
|
|
50
|
+
opacity = 0.15,
|
|
51
|
+
blendMode = 'overlay',
|
|
52
|
+
animated = true,
|
|
53
|
+
speed = 0.3,
|
|
54
|
+
frequency = 0.03,
|
|
55
|
+
amplitude = 0.6,
|
|
56
|
+
cellSize = 16,
|
|
57
|
+
fps = 30,
|
|
58
|
+
class: className = '',
|
|
59
|
+
}: GossamerOverlayProps = $props();
|
|
60
|
+
|
|
61
|
+
// State
|
|
62
|
+
let canvas: HTMLCanvasElement;
|
|
63
|
+
let container: HTMLDivElement;
|
|
64
|
+
let renderer: GossamerRenderer | null = null;
|
|
65
|
+
let isVisible = true;
|
|
66
|
+
let reducedMotion = false;
|
|
67
|
+
let animationId: number | null = null;
|
|
68
|
+
|
|
69
|
+
const shouldAnimate = $derived(animated && isVisible && !reducedMotion);
|
|
70
|
+
|
|
71
|
+
let startTime = 0;
|
|
72
|
+
|
|
73
|
+
function animate(currentTime: number): void {
|
|
74
|
+
if (!renderer || !shouldAnimate) return;
|
|
75
|
+
|
|
76
|
+
const elapsed = (currentTime - startTime) / 1000;
|
|
77
|
+
const { cols, rows } = renderer.getCellCount();
|
|
78
|
+
|
|
79
|
+
const grid = generateBrightnessGrid(
|
|
80
|
+
cols,
|
|
81
|
+
rows,
|
|
82
|
+
pattern,
|
|
83
|
+
elapsed,
|
|
84
|
+
{ frequency, amplitude, speed }
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
renderer.renderFromBrightnessGrid(grid);
|
|
88
|
+
animationId = requestAnimationFrame(animate);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function startAnimation(): void {
|
|
92
|
+
if (animationId !== null) return;
|
|
93
|
+
startTime = performance.now();
|
|
94
|
+
animationId = requestAnimationFrame(animate);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function stopAnimation(): void {
|
|
98
|
+
if (animationId !== null) {
|
|
99
|
+
cancelAnimationFrame(animationId);
|
|
100
|
+
animationId = null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function renderStatic(): void {
|
|
105
|
+
if (!renderer) return;
|
|
106
|
+
|
|
107
|
+
const { cols, rows } = renderer.getCellCount();
|
|
108
|
+
const grid = generateBrightnessGrid(
|
|
109
|
+
cols,
|
|
110
|
+
rows,
|
|
111
|
+
pattern,
|
|
112
|
+
0,
|
|
113
|
+
{ frequency, amplitude, speed: 0 }
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
renderer.renderFromBrightnessGrid(grid);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function setupRenderer(width: number, height: number): void {
|
|
120
|
+
if (!canvas) return;
|
|
121
|
+
|
|
122
|
+
if (renderer) {
|
|
123
|
+
renderer.destroy();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
canvas.width = width;
|
|
127
|
+
canvas.height = height;
|
|
128
|
+
|
|
129
|
+
renderer = new GossamerRenderer(canvas, {
|
|
130
|
+
characters,
|
|
131
|
+
cellWidth: cellSize,
|
|
132
|
+
cellHeight: cellSize,
|
|
133
|
+
color,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
if (shouldAnimate) {
|
|
137
|
+
startAnimation();
|
|
138
|
+
} else {
|
|
139
|
+
renderStatic();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Lifecycle
|
|
144
|
+
onMount(() => {
|
|
145
|
+
const cleanupMotion = onReducedMotionChange((prefers) => {
|
|
146
|
+
reducedMotion = prefers;
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const cleanupVisibility = createVisibilityObserver(
|
|
150
|
+
container,
|
|
151
|
+
(visible) => {
|
|
152
|
+
isVisible = visible;
|
|
153
|
+
if (visible && shouldAnimate) {
|
|
154
|
+
startAnimation();
|
|
155
|
+
} else {
|
|
156
|
+
stopAnimation();
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
0.1
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
const cleanupResize = createResizeObserver(
|
|
163
|
+
container,
|
|
164
|
+
(width, height) => {
|
|
165
|
+
setupRenderer(width, height);
|
|
166
|
+
},
|
|
167
|
+
100
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
const rect = container.getBoundingClientRect();
|
|
171
|
+
if (rect.width > 0 && rect.height > 0) {
|
|
172
|
+
setupRenderer(rect.width, rect.height);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return () => {
|
|
176
|
+
cleanupMotion();
|
|
177
|
+
cleanupVisibility();
|
|
178
|
+
cleanupResize();
|
|
179
|
+
stopAnimation();
|
|
180
|
+
renderer?.destroy();
|
|
181
|
+
};
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
$effect(() => {
|
|
185
|
+
if (renderer) {
|
|
186
|
+
renderer.updateConfig({
|
|
187
|
+
characters,
|
|
188
|
+
color,
|
|
189
|
+
cellWidth: cellSize,
|
|
190
|
+
cellHeight: cellSize,
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
if (shouldAnimate) {
|
|
194
|
+
startAnimation();
|
|
195
|
+
} else {
|
|
196
|
+
stopAnimation();
|
|
197
|
+
renderStatic();
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
</script>
|
|
202
|
+
|
|
203
|
+
<div
|
|
204
|
+
bind:this={container}
|
|
205
|
+
class="gossamer-overlay {className}"
|
|
206
|
+
style:opacity
|
|
207
|
+
style:mix-blend-mode={blendMode}
|
|
208
|
+
>
|
|
209
|
+
<canvas
|
|
210
|
+
bind:this={canvas}
|
|
211
|
+
aria-hidden="true"
|
|
212
|
+
class="gossamer-canvas"
|
|
213
|
+
></canvas>
|
|
214
|
+
</div>
|
|
215
|
+
|
|
216
|
+
<style>
|
|
217
|
+
.gossamer-overlay {
|
|
218
|
+
position: absolute;
|
|
219
|
+
inset: 0;
|
|
220
|
+
width: 100%;
|
|
221
|
+
height: 100%;
|
|
222
|
+
overflow: hidden;
|
|
223
|
+
pointer-events: none;
|
|
224
|
+
z-index: 1;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.gossamer-canvas {
|
|
228
|
+
display: block;
|
|
229
|
+
width: 100%;
|
|
230
|
+
height: 100%;
|
|
231
|
+
}
|
|
232
|
+
</style>
|