@page-speed/pressable 0.0.1
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/LICENSE +28 -0
- package/README.md +417 -0
- package/dist/core/index.cjs +456 -0
- package/dist/core/index.cjs.map +1 -0
- package/dist/core/index.d.cts +70 -0
- package/dist/core/index.d.ts +70 -0
- package/dist/core/index.js +433 -0
- package/dist/core/index.js.map +1 -0
- package/dist/hooks/index.cjs +222 -0
- package/dist/hooks/index.cjs.map +1 -0
- package/dist/hooks/index.d.cts +38 -0
- package/dist/hooks/index.d.ts +38 -0
- package/dist/hooks/index.js +200 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/index-2mStzo0F.d.cts +89 -0
- package/dist/index-2mStzo0F.d.ts +89 -0
- package/dist/index.cjs +458 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +433 -0
- package/dist/index.js.map +1 -0
- package/dist/types/index.cjs +4 -0
- package/dist/types/index.cjs.map +1 -0
- package/dist/types/index.d.cts +4 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/index.cjs +13 -0
- package/dist/utils/index.cjs.map +1 -0
- package/dist/utils/index.d.cts +18 -0
- package/dist/utils/index.d.ts +18 -0
- package/dist/utils/index.js +11 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +103 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025, OpenSite AI. All rights reserved.
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
ADDED
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
# @page-speed/pressable
|
|
2
|
+
|
|
3
|
+
Performance-optimized universal link/button component with automatic URL detection and normalization for the DashTrack ecosystem.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🔗 **Universal Component**: Automatically renders `<a>`, `<button>`, or fallback elements based on props
|
|
8
|
+
- 🌐 **Smart URL Detection**: Automatically detects and normalizes internal, external, mailto, and tel links
|
|
9
|
+
- 📱 **Phone Number Normalization**: Converts various phone formats to standard `tel:` format
|
|
10
|
+
- ✉️ **Email Normalization**: Automatically adds `mailto:` prefix to email addresses
|
|
11
|
+
- 🎨 **ShadCN Button Variants**: Full integration with ShadCN button styles and variants
|
|
12
|
+
- ♿ **Accessibility First**: Proper ARIA attributes, keyboard navigation, and screen reader support
|
|
13
|
+
- 🎯 **SEO Optimized**: Internal links always render as `<a>` tags for proper SEO
|
|
14
|
+
- 🌲 **Tree-Shakable**: Granular exports for minimal bundle size
|
|
15
|
+
- 🚀 **Zero Runtime Overhead**: Efficient memoization and minimal re-renders
|
|
16
|
+
- 🔒 **Type Safe**: Full TypeScript support with comprehensive types
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
\`\`\`bash
|
|
21
|
+
# Using pnpm (recommended)
|
|
22
|
+
pnpm add @page-speed/pressable
|
|
23
|
+
|
|
24
|
+
# Using npm
|
|
25
|
+
npm install @page-speed/pressable
|
|
26
|
+
|
|
27
|
+
# Using yarn
|
|
28
|
+
yarn add @page-speed/pressable
|
|
29
|
+
\`\`\`
|
|
30
|
+
|
|
31
|
+
## Peer Dependencies
|
|
32
|
+
|
|
33
|
+
\`\`\`json
|
|
34
|
+
{
|
|
35
|
+
"react": ">=17.0.0",
|
|
36
|
+
"react-dom": ">=17.0.0"
|
|
37
|
+
}
|
|
38
|
+
\`\`\`
|
|
39
|
+
|
|
40
|
+
## Basic Usage
|
|
41
|
+
|
|
42
|
+
### Simple Link
|
|
43
|
+
|
|
44
|
+
\`\`\`tsx
|
|
45
|
+
import { Pressable } from "@page-speed/pressable";
|
|
46
|
+
|
|
47
|
+
function Navigation() {
|
|
48
|
+
return <Pressable href="/about">About Us</Pressable>;
|
|
49
|
+
}
|
|
50
|
+
\`\`\`
|
|
51
|
+
|
|
52
|
+
### External Link
|
|
53
|
+
|
|
54
|
+
Automatically gets \`target="_blank"\` and \`rel="noopener noreferrer"\`:
|
|
55
|
+
|
|
56
|
+
\`\`\`tsx
|
|
57
|
+
<Pressable href="https://google.com">Visit Google</Pressable>
|
|
58
|
+
\`\`\`
|
|
59
|
+
|
|
60
|
+
### Button-Styled Link
|
|
61
|
+
|
|
62
|
+
\`\`\`tsx
|
|
63
|
+
<Pressable href="/contact" asButton variant="default" size="lg">
|
|
64
|
+
Contact Us
|
|
65
|
+
</Pressable>
|
|
66
|
+
\`\`\`
|
|
67
|
+
|
|
68
|
+
### Phone Link
|
|
69
|
+
|
|
70
|
+
Automatically normalized to \`tel:\` format:
|
|
71
|
+
|
|
72
|
+
\`\`\`tsx
|
|
73
|
+
<Pressable href="(432) 238-6131">Call Us</Pressable>
|
|
74
|
+
// Renders: <a href="tel:+14322386131">Call Us</a>
|
|
75
|
+
\`\`\`
|
|
76
|
+
|
|
77
|
+
### Email Link
|
|
78
|
+
|
|
79
|
+
Automatically normalized to \`mailto:\` format:
|
|
80
|
+
|
|
81
|
+
\`\`\`tsx
|
|
82
|
+
<Pressable href="info@example.com">Email Us</Pressable>
|
|
83
|
+
// Renders: <a href="mailto:info@example.com">Email Us</a>
|
|
84
|
+
\`\`\`
|
|
85
|
+
|
|
86
|
+
### Button with onClick
|
|
87
|
+
|
|
88
|
+
\`\`\`tsx
|
|
89
|
+
<Pressable onClick={() => alert("Clicked")} asButton variant="default">
|
|
90
|
+
Click Me
|
|
91
|
+
</Pressable>
|
|
92
|
+
\`\`\`
|
|
93
|
+
|
|
94
|
+
## Advanced Usage
|
|
95
|
+
|
|
96
|
+
### Button Variants
|
|
97
|
+
|
|
98
|
+
Supports all ShadCN button variants:
|
|
99
|
+
|
|
100
|
+
\`\`\`tsx
|
|
101
|
+
// Default variant
|
|
102
|
+
<Pressable href="/primary" variant="default">Primary</Pressable>
|
|
103
|
+
|
|
104
|
+
// Outline variant
|
|
105
|
+
<Pressable href="/outline" variant="outline">Outline</Pressable>
|
|
106
|
+
|
|
107
|
+
// Secondary variant
|
|
108
|
+
<Pressable href="/secondary" variant="secondary">Secondary</Pressable>
|
|
109
|
+
|
|
110
|
+
// Ghost variant
|
|
111
|
+
<Pressable href="/ghost" variant="ghost">Ghost</Pressable>
|
|
112
|
+
|
|
113
|
+
// Link variant
|
|
114
|
+
<Pressable href="/link" variant="link">Link Style</Pressable>
|
|
115
|
+
|
|
116
|
+
// Destructive variant
|
|
117
|
+
<Pressable href="/delete" variant="destructive">Delete</Pressable>
|
|
118
|
+
\`\`\`
|
|
119
|
+
|
|
120
|
+
### Button Sizes
|
|
121
|
+
|
|
122
|
+
\`\`\`tsx
|
|
123
|
+
<Pressable href="/small" size="sm">Small</Pressable>
|
|
124
|
+
<Pressable href="/default" size="default">Default</Pressable>
|
|
125
|
+
<Pressable href="/medium" size="md">Medium</Pressable>
|
|
126
|
+
<Pressable href="/large" size="lg">Large</Pressable>
|
|
127
|
+
|
|
128
|
+
// Icon sizes
|
|
129
|
+
<Pressable href="/icon" size="icon">
|
|
130
|
+
<Icon />
|
|
131
|
+
</Pressable>
|
|
132
|
+
<Pressable href="/icon-sm" size="icon-sm">
|
|
133
|
+
<Icon />
|
|
134
|
+
</Pressable>
|
|
135
|
+
<Pressable href="/icon-lg" size="icon-lg">
|
|
136
|
+
<Icon />
|
|
137
|
+
</Pressable>
|
|
138
|
+
\`\`\`
|
|
139
|
+
|
|
140
|
+
### Custom Layouts
|
|
141
|
+
|
|
142
|
+
Full control over children:
|
|
143
|
+
|
|
144
|
+
\`\`\`tsx
|
|
145
|
+
<Pressable href="/services" className="flex flex-col gap-4 p-6 rounded-lg border">
|
|
146
|
+
<div className="flex items-center gap-2">
|
|
147
|
+
<ServiceIcon />
|
|
148
|
+
<h3>Our Services</h3>
|
|
149
|
+
</div>
|
|
150
|
+
<p>Learn more about what we offer</p>
|
|
151
|
+
</Pressable>
|
|
152
|
+
\`\`\`
|
|
153
|
+
|
|
154
|
+
### Accessibility
|
|
155
|
+
|
|
156
|
+
\`\`\`tsx
|
|
157
|
+
<Pressable
|
|
158
|
+
href="/important"
|
|
159
|
+
aria-label="Important action"
|
|
160
|
+
aria-describedby="description"
|
|
161
|
+
id="important-link"
|
|
162
|
+
>
|
|
163
|
+
<span id="description">Click here for important information</span>
|
|
164
|
+
</Pressable>
|
|
165
|
+
\`\`\`
|
|
166
|
+
|
|
167
|
+
### Refs
|
|
168
|
+
|
|
169
|
+
\`\`\`tsx
|
|
170
|
+
const linkRef = useRef<HTMLAnchorElement>(null);
|
|
171
|
+
|
|
172
|
+
<Pressable ref={linkRef} href="/ref-example">
|
|
173
|
+
Link with Ref
|
|
174
|
+
</Pressable>
|
|
175
|
+
\`\`\`
|
|
176
|
+
|
|
177
|
+
## API Reference
|
|
178
|
+
|
|
179
|
+
### Props
|
|
180
|
+
|
|
181
|
+
#### Core Props
|
|
182
|
+
|
|
183
|
+
| Prop | Type | Default | Description |
|
|
184
|
+
|------|------|---------|-------------|
|
|
185
|
+
| \`children\` | \`ReactNode\` | - | Content inside the component |
|
|
186
|
+
| \`href\` | \`string\` | - | URL to navigate to (supports internal, external, mailto, tel) |
|
|
187
|
+
| \`onClick\` | \`MouseEventHandler\` | - | Click handler function |
|
|
188
|
+
| \`className\` | \`string\` | - | Additional CSS classes |
|
|
189
|
+
| \`asButton\` | \`boolean\` | \`false\` | Apply button styles even when rendering as \`<a>\` |
|
|
190
|
+
|
|
191
|
+
#### Button Styling
|
|
192
|
+
|
|
193
|
+
| Prop | Type | Default | Description |
|
|
194
|
+
|------|------|---------|-------------|
|
|
195
|
+
| \`variant\` | \`'default' \| 'destructive' \| 'outline' \| 'secondary' \| 'ghost' \| 'link'\` | - | Button variant style |
|
|
196
|
+
| \`size\` | \`'default' \| 'sm' \| 'md' \| 'lg' \| 'icon' \| 'icon-sm' \| 'icon-lg'\` | - | Button size |
|
|
197
|
+
|
|
198
|
+
#### Component Type
|
|
199
|
+
|
|
200
|
+
| Prop | Type | Default | Description |
|
|
201
|
+
|------|------|---------|-------------|
|
|
202
|
+
| \`componentType\` | \`'a' \| 'button' \| 'span' \| 'div'\` | auto | Explicit component type to render |
|
|
203
|
+
| \`fallbackComponentType\` | \`'span' \| 'div' \| 'button'\` | \`'span'\` | Component to render when no href/onClick |
|
|
204
|
+
|
|
205
|
+
#### Accessibility
|
|
206
|
+
|
|
207
|
+
| Prop | Type | Default | Description |
|
|
208
|
+
|------|------|---------|-------------|
|
|
209
|
+
| \`aria-label\` | \`string\` | - | ARIA label for accessibility |
|
|
210
|
+
| \`aria-describedby\` | \`string\` | - | ARIA describedby reference |
|
|
211
|
+
| \`id\` | \`string\` | - | Element ID |
|
|
212
|
+
|
|
213
|
+
#### Data Attributes
|
|
214
|
+
|
|
215
|
+
Any \`data-*\` attributes are automatically forwarded to the rendered element.
|
|
216
|
+
|
|
217
|
+
## URL Detection & Normalization
|
|
218
|
+
|
|
219
|
+
### Internal Links
|
|
220
|
+
|
|
221
|
+
Full URLs matching the current origin are automatically converted to relative paths:
|
|
222
|
+
|
|
223
|
+
\`\`\`tsx
|
|
224
|
+
// On https://example.com
|
|
225
|
+
<Pressable href="https://example.com/about">About</Pressable>
|
|
226
|
+
// Renders: <a href="/about">About</a>
|
|
227
|
+
\`\`\`
|
|
228
|
+
|
|
229
|
+
### Phone Number Formats
|
|
230
|
+
|
|
231
|
+
Supports various phone number formats:
|
|
232
|
+
|
|
233
|
+
\`\`\`tsx
|
|
234
|
+
<Pressable href="(432) 238-6131" /> // → tel:+14322386131
|
|
235
|
+
<Pressable href="512-232-2212" /> // → tel:+5122322212
|
|
236
|
+
<Pressable href="512.232.2212" /> // → tel:+5122322212
|
|
237
|
+
<Pressable href="+1 432 238 6131" /> // → tel:+14322386131
|
|
238
|
+
<Pressable href="512-232-2212x123" /> // → tel:+5122322212;ext=123
|
|
239
|
+
\`\`\`
|
|
240
|
+
|
|
241
|
+
### Email Detection
|
|
242
|
+
|
|
243
|
+
Automatically detects email addresses:
|
|
244
|
+
|
|
245
|
+
\`\`\`tsx
|
|
246
|
+
<Pressable href="hello@example.com" /> // → mailto:hello@example.com
|
|
247
|
+
<Pressable href="mailto:test@ex.com" /> // → mailto:test@ex.com (unchanged)
|
|
248
|
+
\`\`\`
|
|
249
|
+
|
|
250
|
+
## Hooks
|
|
251
|
+
|
|
252
|
+
### useNavigation
|
|
253
|
+
|
|
254
|
+
Low-level hook for custom navigation logic:
|
|
255
|
+
|
|
256
|
+
\`\`\`tsx
|
|
257
|
+
import { useNavigation } from "@page-speed/pressable/hooks";
|
|
258
|
+
|
|
259
|
+
function CustomLink({ href }) {
|
|
260
|
+
const {
|
|
261
|
+
linkType,
|
|
262
|
+
normalizedHref,
|
|
263
|
+
target,
|
|
264
|
+
rel,
|
|
265
|
+
isInternal,
|
|
266
|
+
isExternal,
|
|
267
|
+
handleClick,
|
|
268
|
+
} = useNavigation({ href });
|
|
269
|
+
|
|
270
|
+
return (
|
|
271
|
+
<a href={normalizedHref} target={target} rel={rel} onClick={handleClick}>
|
|
272
|
+
{href}
|
|
273
|
+
</a>
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
\`\`\`
|
|
277
|
+
|
|
278
|
+
#### useNavigation Return Values
|
|
279
|
+
|
|
280
|
+
| Property | Type | Description |
|
|
281
|
+
|----------|------|-------------|
|
|
282
|
+
| \`linkType\` | \`'internal' \| 'external' \| 'mailto' \| 'tel' \| 'none' \| 'unknown'\` | Detected link type |
|
|
283
|
+
| \`normalizedHref\` | \`string \| undefined\` | Normalized URL |
|
|
284
|
+
| \`target\` | \`'_blank' \| '_self' \| undefined\` | Link target attribute |
|
|
285
|
+
| \`rel\` | \`string \| undefined\` | Link rel attribute |
|
|
286
|
+
| \`isInternal\` | \`boolean\` | Whether link is internal |
|
|
287
|
+
| \`isExternal\` | \`boolean\` | Whether link is external |
|
|
288
|
+
| \`shouldUseRouter\` | \`boolean\` | Whether to use client-side routing |
|
|
289
|
+
| \`handleClick\` | \`MouseEventHandler\` | Click handler function |
|
|
290
|
+
|
|
291
|
+
## Utilities
|
|
292
|
+
|
|
293
|
+
### cn
|
|
294
|
+
|
|
295
|
+
Utility for merging Tailwind classes:
|
|
296
|
+
|
|
297
|
+
\`\`\`tsx
|
|
298
|
+
import { cn } from "@page-speed/pressable/utils";
|
|
299
|
+
|
|
300
|
+
function CustomButton() {
|
|
301
|
+
return (
|
|
302
|
+
<Pressable
|
|
303
|
+
href="/test"
|
|
304
|
+
className={cn(
|
|
305
|
+
"base-class",
|
|
306
|
+
isActive && "active-class",
|
|
307
|
+
{ "conditional": someCondition }
|
|
308
|
+
)}
|
|
309
|
+
>
|
|
310
|
+
Custom Button
|
|
311
|
+
</Pressable>
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
\`\`\`
|
|
315
|
+
|
|
316
|
+
## Integration with opensite-blocks
|
|
317
|
+
|
|
318
|
+
The Pressable component integrates seamlessly with the opensite-blocks navigation system:
|
|
319
|
+
|
|
320
|
+
\`\`\`tsx
|
|
321
|
+
// Set up navigation handler (typically done in opensite-blocks)
|
|
322
|
+
window.__opensiteNavigationHandler = (href, event) => {
|
|
323
|
+
// Custom navigation logic (e.g., React Router)
|
|
324
|
+
navigate(href);
|
|
325
|
+
return true; // Indicates navigation was handled
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
// Pressable automatically uses the handler for internal links
|
|
329
|
+
<Pressable href="/about">About</Pressable>
|
|
330
|
+
\`\`\`
|
|
331
|
+
|
|
332
|
+
## CSS Variables
|
|
333
|
+
|
|
334
|
+
The component supports extensive CSS variable customization for button styles. See the [button-variants.ts](./src/core/button-variants.ts) file for the complete list of CSS variables.
|
|
335
|
+
|
|
336
|
+
### Master Variables
|
|
337
|
+
|
|
338
|
+
\`\`\`css
|
|
339
|
+
:root {
|
|
340
|
+
--button-font-family: inherit;
|
|
341
|
+
--button-font-weight: 500;
|
|
342
|
+
--button-letter-spacing: 0;
|
|
343
|
+
--button-line-height: 1.25;
|
|
344
|
+
--button-text-transform: none;
|
|
345
|
+
--button-transition: all 250ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
346
|
+
--button-radius: 0.375rem;
|
|
347
|
+
--button-shadow: none;
|
|
348
|
+
--button-shadow-hover: none;
|
|
349
|
+
}
|
|
350
|
+
\`\`\`
|
|
351
|
+
|
|
352
|
+
### Per-Variant Variables
|
|
353
|
+
|
|
354
|
+
\`\`\`css
|
|
355
|
+
:root {
|
|
356
|
+
/* Default variant */
|
|
357
|
+
--button-default-bg: hsl(var(--primary));
|
|
358
|
+
--button-default-fg: hsl(var(--primary-foreground));
|
|
359
|
+
--button-default-hover-bg: hsl(var(--primary) / 0.9);
|
|
360
|
+
|
|
361
|
+
/* Outline variant */
|
|
362
|
+
--button-outline-bg: hsl(var(--background));
|
|
363
|
+
--button-outline-border: hsl(var(--border));
|
|
364
|
+
--button-outline-border-width: 1px;
|
|
365
|
+
|
|
366
|
+
/* ... and more */
|
|
367
|
+
}
|
|
368
|
+
\`\`\`
|
|
369
|
+
|
|
370
|
+
## Tree-Shaking
|
|
371
|
+
|
|
372
|
+
The package is fully tree-shakable. Import only what you need:
|
|
373
|
+
|
|
374
|
+
\`\`\`tsx
|
|
375
|
+
// Import specific components
|
|
376
|
+
import { Pressable } from "@page-speed/pressable/core";
|
|
377
|
+
import { useNavigation } from "@page-speed/pressable/hooks";
|
|
378
|
+
import { cn } from "@page-speed/pressable/utils";
|
|
379
|
+
|
|
380
|
+
// Or use granular imports
|
|
381
|
+
import { Pressable } from "@page-speed/pressable/core/Pressable";
|
|
382
|
+
import { buttonVariants } from "@page-speed/pressable/core/button-variants";
|
|
383
|
+
\`\`\`
|
|
384
|
+
|
|
385
|
+
## Performance
|
|
386
|
+
|
|
387
|
+
- **Bundle Size**: ~8KB gzipped (including all dependencies)
|
|
388
|
+
- **Tree-Shaking**: Unused code is automatically eliminated
|
|
389
|
+
- **Memoization**: All computed values are memoized with React.useMemo
|
|
390
|
+
- **Zero Runtime Overhead**: Efficient URL detection and normalization
|
|
391
|
+
- **SSR Compatible**: Works seamlessly with server-side rendering
|
|
392
|
+
|
|
393
|
+
## Browser Support
|
|
394
|
+
|
|
395
|
+
- Modern browsers (Chrome, Firefox, Safari, Edge)
|
|
396
|
+
- React 17+
|
|
397
|
+
- Server-side rendering (SSR)
|
|
398
|
+
- Static site generation (SSG)
|
|
399
|
+
|
|
400
|
+
## License
|
|
401
|
+
|
|
402
|
+
MIT
|
|
403
|
+
|
|
404
|
+
## Contributing
|
|
405
|
+
|
|
406
|
+
Contributions are welcome! Please follow the [DashTrack ecosystem guidelines](./docs/ECOSYSTEM_GUIDELINES.md).
|
|
407
|
+
|
|
408
|
+
## Related Packages
|
|
409
|
+
|
|
410
|
+
- [@page-speed/img](https://www.npmjs.com/package/@page-speed/img) - Performance-optimized image component
|
|
411
|
+
- [@page-speed/markdown-to-jsx](https://www.npmjs.com/package/@page-speed/markdown-to-jsx) - Markdown renderer with Pressable integration
|
|
412
|
+
- [@opensite/blocks](https://www.npmjs.com/package/@opensite/blocks) - Chai design payload renderer
|
|
413
|
+
|
|
414
|
+
## Support
|
|
415
|
+
|
|
416
|
+
- [GitHub Issues](https://github.com/opensite-ai/pressable/issues)
|
|
417
|
+
- [DashTrack Documentation](https://docs.dashtrack.com)
|