this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

more slop

+1282
+118
webcomp/BUSHEL.md
··· 1 + # Bushel Schemas 2 + 3 + Bushel is a webring platform for collaborative online content sharing, described as "a reconstruction of LiveJournal for the 2020s." It enables open source communities to track information across multiple communication channels with privacy controls. 4 + 5 + ## Overview 6 + 7 + Based on the repository structure and documentation, Bushel provides: 8 + 9 + - **Collaborative Content Sharing**: Individuals can quickly write about their work 10 + - **Bidirectional Linking**: Groups can share content with interconnected references 11 + - **Version-Controlled Content**: Supports versioned data structures like blog posts, wiki entries, and social media feeds 12 + - **Privacy Management**: GitHub OAuth integration for controlling access to private content 13 + - **Resolvable Markdown**: Uses enhanced Markdown for sharing structured data 14 + 15 + ## Core Schema Modules 16 + 17 + The library is organized into several key modules: 18 + 19 + ### Contact Schema 20 + 21 + The `contact` module likely defines structures for: 22 + 23 + ```ocaml 24 + (* Hypothetical contact schema based on social networking requirements *) 25 + type contact = { 26 + id: string; 27 + name: string; 28 + email: string option; 29 + github_username: string option; 30 + website: string option; 31 + bio: string option; 32 + avatar_url: string option; 33 + public_key: string option; 34 + } 35 + 36 + type social_link = { 37 + platform: string; (* github, mastodon, twitter, etc *) 38 + handle: string; 39 + url: string; 40 + } 41 + 42 + type contact_with_social = { 43 + contact: contact; 44 + social_links: social_link list; 45 + trusted: bool; (* for privacy/trust relationships *) 46 + } 47 + ``` 48 + 49 + ### Entry Schema 50 + 51 + The `entry` module represents content items: 52 + - Blog posts 53 + - Wiki entries 54 + - Notes 55 + - Ideas 56 + - News items 57 + 58 + ### Project Schema 59 + 60 + The `project` module handles: 61 + - Open source project metadata 62 + - Collaboration settings 63 + - Privacy controls 64 + - Version tracking 65 + 66 + ### Link Schema 67 + 68 + The `link` module manages: 69 + - Bidirectional references between content 70 + - External links with metadata 71 + - Cross-references between users and projects 72 + 73 + ## Contact-Focused Features 74 + 75 + ### Identity Management 76 + - GitHub OAuth integration for authentication 77 + - Support for multiple identity providers 78 + - Public key infrastructure for secure communication 79 + 80 + ### Social Networking 81 + - Contact discovery within communities 82 + - Trust relationships for privacy 83 + - Cross-platform identity linking 84 + 85 + ### Communication Channels 86 + - Multi-channel content aggregation 87 + - Private design discussions 88 + - Public collaboration spaces 89 + 90 + ### Privacy Controls 91 + - Individual privacy settings per contact 92 + - Group-based access controls 93 + - Selective content sharing 94 + 95 + ## Markdown Extensions 96 + 97 + Bushel extends standard Markdown with special syntax: 98 + - `@avsm/bushel` - Reference to users/projects 99 + - Support for structured data embedding 100 + - Version-controlled content references 101 + 102 + ## Technical Architecture 103 + 104 + - **Language**: Primarily OCaml (77%) with Python utilities (22.5%) 105 + - **Content Format**: Enhanced Markdown with structured data 106 + - **Versioning**: Git-based version control for all content 107 + - **Authentication**: GitHub OAuth with extensible identity providers 108 + - **Privacy**: Granular access controls and trust relationships 109 + 110 + ## Use Cases 111 + 112 + 1. **Open Source Communities**: Track contributions across multiple channels 113 + 2. **Collaborative Writing**: Shared documentation with version control 114 + 3. **Social Networking**: Privacy-aware community building 115 + 4. **Content Aggregation**: Unified view of distributed content 116 + 5. **Project Management**: Cross-project collaboration and references 117 + 118 + The contact schema is central to enabling these use cases by providing a foundation for identity, trust, and communication within the Bushel ecosystem.
+3
webcomp/CLAUDE.md
··· 1 + I'd like to experiment with webcomponents and build up a website entirely composed around the principled use of them, with semantically meaningful tags and layout templates that are translated to appropriate html. 2 + 3 + We'll start by building up a library of small components based on the bushel schemas in https://github.com/avsm/bushel
+356
webcomp/bushel-author-demo.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 + <title>Bushel Author Component Demo</title> 7 + <script src="bushel-author.js"></script> 8 + <style> 9 + body { 10 + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; 11 + max-width: 1200px; 12 + margin: 0 auto; 13 + padding: 2rem; 14 + line-height: 1.6; 15 + } 16 + 17 + .demo-section { 18 + margin: 3rem 0; 19 + padding: 2rem; 20 + border: 1px solid #e9ecef; 21 + border-radius: 0.5rem; 22 + background: #f8f9fa; 23 + } 24 + 25 + .demo-section h2 { 26 + margin-top: 0; 27 + color: #495057; 28 + } 29 + 30 + .demo-grid { 31 + display: grid; 32 + gap: 2rem; 33 + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); 34 + margin-top: 1.5rem; 35 + } 36 + 37 + .demo-item { 38 + padding: 1rem; 39 + background: white; 40 + border-radius: 0.25rem; 41 + border: 1px solid #dee2e6; 42 + } 43 + 44 + .demo-item h3 { 45 + margin-top: 0; 46 + font-size: 1rem; 47 + color: #6c757d; 48 + } 49 + 50 + pre { 51 + background: #f1f3f4; 52 + padding: 1rem; 53 + border-radius: 0.25rem; 54 + overflow-x: auto; 55 + font-size: 0.875rem; 56 + } 57 + 58 + .custom-styles { 59 + --bushel-author-name-color: #d63384; 60 + --bushel-author-photo-radius: 0.25rem; 61 + --bushel-author-background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); 62 + --bushel-author-padding: 1rem; 63 + --bushel-author-border-radius: 0.5rem; 64 + color: white; 65 + } 66 + </style> 67 + </head> 68 + <body> 69 + <h1>Bushel Author Web Component Demo</h1> 70 + 71 + <p>This component generates semantic HTML with h-card microformats for author information, 72 + automatically expanding simple attributes into rich, accessible markup.</p> 73 + 74 + <div class="demo-section"> 75 + <h2>Basic Usage</h2> 76 + <div class="demo-grid"> 77 + <div class="demo-item"> 78 + <h3>Minimal Example</h3> 79 + <bushel-author 80 + name="Jane Smith" 81 + github="janesmith"> 82 + </bushel-author> 83 + 84 + <pre>&lt;bushel-author 85 + name="Jane Smith" 86 + github="janesmith"&gt; 87 + &lt;/bushel-author&gt;</pre> 88 + </div> 89 + 90 + <div class="demo-item"> 91 + <h3>With Photo and Links</h3> 92 + <bushel-author 93 + name="Alex Chen" 94 + photo="https://picsum.photos/200/200?random=1" 95 + website="https://alexchen.dev" 96 + github="alexchen" 97 + mastodon="https://mastodon.social/@alexchen" 98 + link-profile> 99 + </bushel-author> 100 + 101 + <pre>&lt;bushel-author 102 + name="Alex Chen" 103 + photo="https://picsum.photos/200/200?random=1" 104 + website="https://alexchen.dev" 105 + github="alexchen" 106 + mastodon="https://mastodon.social/@alexchen" 107 + link-profile&gt; 108 + &lt;/bushel-author&gt;</pre> 109 + </div> 110 + </div> 111 + </div> 112 + 113 + <div class="demo-section"> 114 + <h2>Display Modes</h2> 115 + <div class="demo-grid"> 116 + <div class="demo-item"> 117 + <h3>Inline (Default)</h3> 118 + <bushel-author 119 + name="Dr. Sarah Johnson" 120 + job-title="Senior Researcher" 121 + org="MIT" 122 + photo="https://picsum.photos/200/200?random=2" 123 + email="sarah@mit.edu"> 124 + </bushel-author> 125 + 126 + <pre>&lt;bushel-author 127 + name="Dr. Sarah Johnson" 128 + job-title="Senior Researcher" 129 + org="MIT" 130 + photo="https://picsum.photos/200/200?random=2" 131 + email="sarah@mit.edu"&gt; 132 + &lt;/bushel-author&gt;</pre> 133 + </div> 134 + 135 + <div class="demo-item"> 136 + <h3>Card Mode</h3> 137 + <bushel-author 138 + display-mode="card" 139 + name="Marcus Williams" 140 + job-title="Frontend Developer" 141 + org="TechCorp" 142 + photo="https://picsum.photos/200/200?random=3" 143 + bio="Building accessible web experiences with modern technologies." 144 + github="marcusw" 145 + twitter="marcusw_dev"> 146 + </bushel-author> 147 + 148 + <pre>&lt;bushel-author 149 + display-mode="card" 150 + name="Marcus Williams" 151 + job-title="Frontend Developer" 152 + org="TechCorp" 153 + photo="https://picsum.photos/200/200?random=3" 154 + bio="Building accessible web experiences." 155 + github="marcusw" 156 + twitter="marcusw_dev"&gt; 157 + &lt;/bushel-author&gt;</pre> 158 + </div> 159 + 160 + <div class="demo-item"> 161 + <h3>Compact Mode</h3> 162 + <bushel-author 163 + display-mode="compact" 164 + name="Emma Rodriguez" 165 + photo="https://picsum.photos/200/200?random=4" 166 + github="emmarodriguez"> 167 + </bushel-author> 168 + 169 + <pre>&lt;bushel-author 170 + display-mode="compact" 171 + name="Emma Rodriguez" 172 + photo="https://picsum.photos/200/200?random=4" 173 + github="emmarodriguez"&gt; 174 + &lt;/bushel-author&gt;</pre> 175 + </div> 176 + </div> 177 + </div> 178 + 179 + <div class="demo-section"> 180 + <h2>Structured Names</h2> 181 + <div class="demo-grid"> 182 + <div class="demo-item"> 183 + <h3>Separate Name Parts</h3> 184 + <bushel-author 185 + given-name="Robert" 186 + family-name="Taylor" 187 + nickname="Bob" 188 + photo="https://picsum.photos/200/200?random=5" 189 + job-title="UX Designer"> 190 + </bushel-author> 191 + 192 + <pre>&lt;bushel-author 193 + given-name="Robert" 194 + family-name="Taylor" 195 + nickname="Bob" 196 + photo="https://picsum.photos/200/200?random=5" 197 + job-title="UX Designer"&gt; 198 + &lt;/bushel-author&gt;</pre> 199 + 200 + <p><small>Note: Structured name parts are embedded as hidden microformat data while displaying the formatted name.</small></p> 201 + </div> 202 + </div> 203 + </div> 204 + 205 + <div class="demo-section"> 206 + <h2>Custom Styling</h2> 207 + <div class="demo-grid"> 208 + <div class="demo-item"> 209 + <h3>CSS Custom Properties</h3> 210 + <bushel-author 211 + class="custom-styles" 212 + name="Luna Zhang" 213 + job-title="Creative Director" 214 + photo="https://picsum.photos/200/200?random=6" 215 + website="https://lunazhang.com" 216 + link-profile> 217 + </bushel-author> 218 + 219 + <pre>&lt;bushel-author 220 + class="custom-styles" 221 + name="Luna Zhang" 222 + job-title="Creative Director" 223 + photo="https://picsum.photos/200/200?random=6" 224 + website="https://lunazhang.com" 225 + link-profile&gt; 226 + &lt;/bushel-author&gt; 227 + 228 + &lt;style&gt; 229 + .custom-styles { 230 + --bushel-author-name-color: #d63384; 231 + --bushel-author-photo-radius: 0.25rem; 232 + --bushel-author-background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); 233 + --bushel-author-padding: 1rem; 234 + --bushel-author-border-radius: 0.5rem; 235 + color: white; 236 + } 237 + &lt;/style&gt;</pre> 238 + </div> 239 + </div> 240 + </div> 241 + 242 + <div class="demo-section"> 243 + <h2>Generated HTML Structure</h2> 244 + <p>Each component generates semantic HTML with h-card microformats:</p> 245 + <pre>&lt;div class="h-card bushel-author-card"&gt; 246 + &lt;img class="u-photo author-photo" src="..." alt="Photo of John Doe"&gt; 247 + &lt;div class="author-info"&gt; 248 + &lt;a class="p-name author-name u-url" href="https://johndoe.com"&gt; 249 + John Doe 250 + &lt;span class="p-given-name visually-hidden"&gt;John&lt;/span&gt; 251 + &lt;span class="p-family-name visually-hidden"&gt;Doe&lt;/span&gt; 252 + &lt;/a&gt; 253 + &lt;div class="author-title"&gt; 254 + &lt;span class="p-job-title"&gt;Software Engineer&lt;/span&gt; at 255 + &lt;span class="p-org"&gt;Acme Corp&lt;/span&gt; 256 + &lt;/div&gt; 257 + &lt;div class="p-note author-bio"&gt;Building great software.&lt;/div&gt; 258 + &lt;div class="author-social"&gt; 259 + &lt;a class="u-email social-link" href="mailto:john@example.com"&gt;Email&lt;/a&gt; 260 + &lt;a class="u-url social-link" href="https://github.com/johndoe" rel="me"&gt;GitHub&lt;/a&gt; 261 + &lt;/div&gt; 262 + &lt;/div&gt; 263 + &lt;/div&gt;</pre> 264 + </div> 265 + 266 + <div class="demo-section"> 267 + <h2>Available Attributes</h2> 268 + <table style="width: 100%; border-collapse: collapse; margin-top: 1rem;"> 269 + <thead> 270 + <tr style="background: #e9ecef;"> 271 + <th style="padding: 0.75rem; text-align: left; border: 1px solid #dee2e6;">Attribute</th> 272 + <th style="padding: 0.75rem; text-align: left; border: 1px solid #dee2e6;">Description</th> 273 + <th style="padding: 0.75rem; text-align: left; border: 1px solid #dee2e6;">Microformat Class</th> 274 + </tr> 275 + </thead> 276 + <tbody> 277 + <tr> 278 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;"><code>name</code></td> 279 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;">Full display name</td> 280 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;"><code>p-name</code></td> 281 + </tr> 282 + <tr style="background: #f8f9fa;"> 283 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;"><code>given-name</code></td> 284 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;">First name</td> 285 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;"><code>p-given-name</code></td> 286 + </tr> 287 + <tr> 288 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;"><code>family-name</code></td> 289 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;">Last name</td> 290 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;"><code>p-family-name</code></td> 291 + </tr> 292 + <tr style="background: #f8f9fa;"> 293 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;"><code>nickname</code></td> 294 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;">Alias or handle</td> 295 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;"><code>p-nickname</code></td> 296 + </tr> 297 + <tr> 298 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;"><code>email</code></td> 299 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;">Email address</td> 300 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;"><code>u-email</code></td> 301 + </tr> 302 + <tr style="background: #f8f9fa;"> 303 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;"><code>website</code></td> 304 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;">Personal website URL</td> 305 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;"><code>u-url</code></td> 306 + </tr> 307 + <tr> 308 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;"><code>photo</code></td> 309 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;">Profile photo URL</td> 310 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;"><code>u-photo</code></td> 311 + </tr> 312 + <tr style="background: #f8f9fa;"> 313 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;"><code>bio</code></td> 314 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;">Biography or description</td> 315 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;"><code>p-note</code></td> 316 + </tr> 317 + <tr> 318 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;"><code>org</code></td> 319 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;">Organization name</td> 320 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;"><code>p-org</code></td> 321 + </tr> 322 + <tr style="background: #f8f9fa;"> 323 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;"><code>job-title</code></td> 324 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;">Job title or role</td> 325 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;"><code>p-job-title</code></td> 326 + </tr> 327 + <tr> 328 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;"><code>github</code></td> 329 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;">GitHub username</td> 330 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;"><code>u-url</code></td> 331 + </tr> 332 + <tr style="background: #f8f9fa;"> 333 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;"><code>mastodon</code></td> 334 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;">Full Mastodon profile URL</td> 335 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;"><code>u-url</code></td> 336 + </tr> 337 + <tr> 338 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;"><code>twitter</code></td> 339 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;">Twitter username</td> 340 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;"><code>u-url</code></td> 341 + </tr> 342 + <tr style="background: #f8f9fa;"> 343 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;"><code>display-mode</code></td> 344 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;">Layout: inline, card, compact</td> 345 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;">-</td> 346 + </tr> 347 + <tr> 348 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;"><code>link-profile</code></td> 349 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;">Make name clickable to website</td> 350 + <td style="padding: 0.75rem; border: 1px solid #dee2e6;">-</td> 351 + </tr> 352 + </tbody> 353 + </table> 354 + </div> 355 + </body> 356 + </html>
+327
webcomp/bushel-author.js
··· 1 + class BushelAuthor extends HTMLElement { 2 + constructor() { 3 + super(); 4 + this.attachShadow({ mode: 'open' }); 5 + } 6 + 7 + static get observedAttributes() { 8 + return [ 9 + 'name', 'given-name', 'family-name', 'nickname', 10 + 'email', 'website', 'github', 'mastodon', 'twitter', 11 + 'photo', 'bio', 'org', 'job-title', 12 + 'display-mode', 'link-profile' 13 + ]; 14 + } 15 + 16 + connectedCallback() { 17 + this.render(); 18 + } 19 + 20 + attributeChangedCallback() { 21 + this.render(); 22 + } 23 + 24 + get displayMode() { 25 + return this.getAttribute('display-mode') || 'inline'; 26 + } 27 + 28 + get shouldLinkProfile() { 29 + return this.hasAttribute('link-profile'); 30 + } 31 + 32 + buildHCard() { 33 + const name = this.getAttribute('name'); 34 + const givenName = this.getAttribute('given-name'); 35 + const familyName = this.getAttribute('family-name'); 36 + const nickname = this.getAttribute('nickname'); 37 + const email = this.getAttribute('email'); 38 + const website = this.getAttribute('website'); 39 + const github = this.getAttribute('github'); 40 + const mastodon = this.getAttribute('mastodon'); 41 + const twitter = this.getAttribute('twitter'); 42 + const photo = this.getAttribute('photo'); 43 + const bio = this.getAttribute('bio'); 44 + const org = this.getAttribute('org'); 45 + const jobTitle = this.getAttribute('job-title'); 46 + 47 + const displayName = name || `${givenName || ''} ${familyName || ''}`.trim() || nickname; 48 + 49 + const hcard = document.createElement('div'); 50 + hcard.className = 'h-card bushel-author-card'; 51 + 52 + // Photo 53 + if (photo) { 54 + const img = document.createElement('img'); 55 + img.className = 'u-photo author-photo'; 56 + img.src = photo; 57 + img.alt = `Photo of ${displayName}`; 58 + img.loading = 'lazy'; 59 + hcard.appendChild(img); 60 + } 61 + 62 + const infoContainer = document.createElement('div'); 63 + infoContainer.className = 'author-info'; 64 + 65 + // Name (with optional link) 66 + const nameElement = document.createElement(this.shouldLinkProfile && website ? 'a' : 'span'); 67 + nameElement.className = 'p-name author-name'; 68 + nameElement.textContent = displayName; 69 + 70 + if (this.shouldLinkProfile && website) { 71 + nameElement.href = website; 72 + nameElement.className += ' u-url'; 73 + } 74 + 75 + // Hidden structured name parts 76 + if (givenName) { 77 + const givenSpan = document.createElement('span'); 78 + givenSpan.className = 'p-given-name visually-hidden'; 79 + givenSpan.textContent = givenName; 80 + nameElement.appendChild(givenSpan); 81 + } 82 + 83 + if (familyName) { 84 + const familySpan = document.createElement('span'); 85 + familySpan.className = 'p-family-name visually-hidden'; 86 + familySpan.textContent = familyName; 87 + nameElement.appendChild(familySpan); 88 + } 89 + 90 + if (nickname && nickname !== displayName) { 91 + const nickSpan = document.createElement('span'); 92 + nickSpan.className = 'p-nickname visually-hidden'; 93 + nickSpan.textContent = nickname; 94 + nameElement.appendChild(nickSpan); 95 + } 96 + 97 + infoContainer.appendChild(nameElement); 98 + 99 + // Job title and organization 100 + if (jobTitle || org) { 101 + const titleElement = document.createElement('div'); 102 + titleElement.className = 'author-title'; 103 + 104 + if (jobTitle) { 105 + const jobSpan = document.createElement('span'); 106 + jobSpan.className = 'p-job-title'; 107 + jobSpan.textContent = jobTitle; 108 + titleElement.appendChild(jobSpan); 109 + 110 + if (org) { 111 + titleElement.appendChild(document.createTextNode(' at ')); 112 + } 113 + } 114 + 115 + if (org) { 116 + const orgSpan = document.createElement('span'); 117 + orgSpan.className = 'p-org'; 118 + orgSpan.textContent = org; 119 + titleElement.appendChild(orgSpan); 120 + } 121 + 122 + infoContainer.appendChild(titleElement); 123 + } 124 + 125 + // Bio/note 126 + if (bio) { 127 + const bioElement = document.createElement('div'); 128 + bioElement.className = 'p-note author-bio'; 129 + bioElement.textContent = bio; 130 + infoContainer.appendChild(bioElement); 131 + } 132 + 133 + // Social links 134 + const socialContainer = document.createElement('div'); 135 + socialContainer.className = 'author-social'; 136 + let hasSocial = false; 137 + 138 + if (email) { 139 + const emailLink = document.createElement('a'); 140 + emailLink.className = 'u-email social-link'; 141 + emailLink.href = `mailto:${email}`; 142 + emailLink.textContent = 'Email'; 143 + emailLink.setAttribute('aria-label', `Email ${displayName}`); 144 + socialContainer.appendChild(emailLink); 145 + hasSocial = true; 146 + } 147 + 148 + if (github) { 149 + const githubLink = document.createElement('a'); 150 + githubLink.className = 'u-url social-link'; 151 + githubLink.href = `https://github.com/${github}`; 152 + githubLink.textContent = 'GitHub'; 153 + githubLink.setAttribute('aria-label', `${displayName} on GitHub`); 154 + githubLink.rel = 'me'; 155 + socialContainer.appendChild(githubLink); 156 + hasSocial = true; 157 + } 158 + 159 + if (mastodon) { 160 + const mastodonLink = document.createElement('a'); 161 + mastodonLink.className = 'u-url social-link'; 162 + mastodonLink.href = mastodon; 163 + mastodonLink.textContent = 'Mastodon'; 164 + mastodonLink.setAttribute('aria-label', `${displayName} on Mastodon`); 165 + mastodonLink.rel = 'me'; 166 + socialContainer.appendChild(mastodonLink); 167 + hasSocial = true; 168 + } 169 + 170 + if (twitter) { 171 + const twitterLink = document.createElement('a'); 172 + twitterLink.className = 'u-url social-link'; 173 + twitterLink.href = `https://twitter.com/${twitter}`; 174 + twitterLink.textContent = 'Twitter'; 175 + twitterLink.setAttribute('aria-label', `${displayName} on Twitter`); 176 + twitterLink.rel = 'me'; 177 + socialContainer.appendChild(twitterLink); 178 + hasSocial = true; 179 + } 180 + 181 + if (website && !this.shouldLinkProfile) { 182 + const websiteLink = document.createElement('a'); 183 + websiteLink.className = 'u-url social-link'; 184 + websiteLink.href = website; 185 + websiteLink.textContent = 'Website'; 186 + websiteLink.setAttribute('aria-label', `${displayName}'s website`); 187 + socialContainer.appendChild(websiteLink); 188 + hasSocial = true; 189 + } 190 + 191 + if (hasSocial) { 192 + infoContainer.appendChild(socialContainer); 193 + } 194 + 195 + hcard.appendChild(infoContainer); 196 + return hcard; 197 + } 198 + 199 + render() { 200 + const styles = ` 201 + <style> 202 + :host { 203 + display: var(--bushel-author-display, inline-block); 204 + font-family: var(--bushel-author-font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif); 205 + } 206 + 207 + .h-card { 208 + display: flex; 209 + align-items: var(--bushel-author-align, flex-start); 210 + gap: var(--bushel-author-gap, 0.75rem); 211 + padding: var(--bushel-author-padding, 0); 212 + background: var(--bushel-author-background, transparent); 213 + border: var(--bushel-author-border, none); 214 + border-radius: var(--bushel-author-border-radius, 0); 215 + } 216 + 217 + :host([display-mode="card"]) .h-card { 218 + flex-direction: column; 219 + text-align: center; 220 + padding: var(--bushel-author-card-padding, 1rem); 221 + background: var(--bushel-author-card-background, #f8f9fa); 222 + border: var(--bushel-author-card-border, 1px solid #e9ecef); 223 + border-radius: var(--bushel-author-card-border-radius, 0.5rem); 224 + } 225 + 226 + :host([display-mode="compact"]) .h-card { 227 + gap: var(--bushel-author-compact-gap, 0.5rem); 228 + } 229 + 230 + .author-photo { 231 + width: var(--bushel-author-photo-size, 2.5rem); 232 + height: var(--bushel-author-photo-size, 2.5rem); 233 + border-radius: var(--bushel-author-photo-radius, 50%); 234 + object-fit: cover; 235 + flex-shrink: 0; 236 + } 237 + 238 + :host([display-mode="card"]) .author-photo { 239 + width: var(--bushel-author-card-photo-size, 4rem); 240 + height: var(--bushel-author-card-photo-size, 4rem); 241 + } 242 + 243 + .author-info { 244 + display: flex; 245 + flex-direction: column; 246 + gap: var(--bushel-author-info-gap, 0.25rem); 247 + min-width: 0; 248 + } 249 + 250 + .author-name { 251 + font-weight: var(--bushel-author-name-weight, 600); 252 + color: var(--bushel-author-name-color, inherit); 253 + text-decoration: none; 254 + font-size: var(--bushel-author-name-size, 1rem); 255 + } 256 + 257 + .author-name:hover { 258 + text-decoration: underline; 259 + } 260 + 261 + .author-title { 262 + font-size: var(--bushel-author-title-size, 0.875rem); 263 + color: var(--bushel-author-title-color, #666); 264 + } 265 + 266 + .author-bio { 267 + font-size: var(--bushel-author-bio-size, 0.875rem); 268 + color: var(--bushel-author-bio-color, #555); 269 + line-height: 1.4; 270 + } 271 + 272 + .author-social { 273 + display: flex; 274 + gap: var(--bushel-author-social-gap, 0.75rem); 275 + margin-top: var(--bushel-author-social-margin, 0.25rem); 276 + } 277 + 278 + :host([display-mode="card"]) .author-social { 279 + justify-content: center; 280 + } 281 + 282 + :host([display-mode="compact"]) .author-social { 283 + gap: var(--bushel-author-compact-social-gap, 0.5rem); 284 + } 285 + 286 + .social-link { 287 + font-size: var(--bushel-author-social-size, 0.8125rem); 288 + color: var(--bushel-author-social-color, #007bff); 289 + text-decoration: none; 290 + padding: var(--bushel-author-social-padding, 0.125rem 0.25rem); 291 + border-radius: var(--bushel-author-social-radius, 0.25rem); 292 + } 293 + 294 + .social-link:hover { 295 + background: var(--bushel-author-social-hover-bg, rgba(0, 123, 255, 0.1)); 296 + text-decoration: none; 297 + } 298 + 299 + .visually-hidden { 300 + position: absolute !important; 301 + width: 1px !important; 302 + height: 1px !important; 303 + padding: 0 !important; 304 + margin: -1px !important; 305 + overflow: hidden !important; 306 + clip: rect(0, 0, 0, 0) !important; 307 + white-space: nowrap !important; 308 + border: 0 !important; 309 + } 310 + 311 + @media (max-width: 600px) { 312 + :host([display-mode="card"]) .h-card { 313 + padding: var(--bushel-author-mobile-padding, 0.75rem); 314 + } 315 + 316 + .author-social { 317 + flex-wrap: wrap; 318 + } 319 + } 320 + </style> 321 + `; 322 + 323 + this.shadowRoot.innerHTML = styles + this.buildHCard().outerHTML; 324 + } 325 + } 326 + 327 + customElements.define('bushel-author', BushelAuthor);
+478
webcomp/index.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 + <title>Bushel Author Components - Real World Examples</title> 7 + <script src="bushel-author.js"></script> 8 + <style> 9 + * { 10 + margin: 0; 11 + padding: 0; 12 + box-sizing: border-box; 13 + } 14 + 15 + body { 16 + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; 17 + line-height: 1.6; 18 + color: #333; 19 + background: #fafafa; 20 + } 21 + 22 + header { 23 + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); 24 + color: white; 25 + padding: 3rem 1rem; 26 + text-align: center; 27 + } 28 + 29 + header h1 { 30 + font-size: 2.5rem; 31 + margin-bottom: 0.5rem; 32 + } 33 + 34 + header p { 35 + font-size: 1.1rem; 36 + opacity: 0.9; 37 + max-width: 600px; 38 + margin: 0 auto; 39 + } 40 + 41 + main { 42 + max-width: 1200px; 43 + margin: 0 auto; 44 + padding: 0 1rem; 45 + } 46 + 47 + .section { 48 + background: white; 49 + margin: 3rem 0; 50 + padding: 2.5rem; 51 + border-radius: 0.75rem; 52 + box-shadow: 0 2px 8px rgba(0,0,0,0.1); 53 + } 54 + 55 + .section h2 { 56 + color: #495057; 57 + margin-bottom: 1.5rem; 58 + font-size: 1.75rem; 59 + } 60 + 61 + .section p { 62 + margin-bottom: 1.5rem; 63 + color: #666; 64 + } 65 + 66 + /* Blog post layout */ 67 + .blog-post { 68 + border-left: 4px solid #667eea; 69 + padding-left: 1.5rem; 70 + margin: 2rem 0; 71 + } 72 + 73 + .blog-post h3 { 74 + color: #495057; 75 + margin-bottom: 0.5rem; 76 + } 77 + 78 + .blog-post .meta { 79 + display: flex; 80 + align-items: center; 81 + gap: 1rem; 82 + margin: 1rem 0; 83 + padding: 1rem 0; 84 + border-top: 1px solid #e9ecef; 85 + border-bottom: 1px solid #e9ecef; 86 + } 87 + 88 + .blog-post .content { 89 + color: #555; 90 + font-size: 1.1rem; 91 + } 92 + 93 + /* Team grid */ 94 + .team-grid { 95 + display: grid; 96 + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); 97 + gap: 2rem; 98 + margin-top: 2rem; 99 + } 100 + 101 + .team-member { 102 + background: #f8f9fa; 103 + padding: 1.5rem; 104 + border-radius: 0.5rem; 105 + text-align: center; 106 + } 107 + 108 + /* Author bylines */ 109 + .article-byline { 110 + display: flex; 111 + align-items: center; 112 + gap: 1rem; 113 + padding: 1rem; 114 + background: #f8f9fa; 115 + border-radius: 0.5rem; 116 + margin: 1rem 0; 117 + } 118 + 119 + .article-content { 120 + background: white; 121 + padding: 2rem; 122 + border-radius: 0.5rem; 123 + border: 1px solid #e9ecef; 124 + margin-top: 1rem; 125 + } 126 + 127 + /* Comment thread */ 128 + .comment-thread { 129 + background: #f8f9fa; 130 + border-radius: 0.5rem; 131 + padding: 1.5rem; 132 + margin: 1rem 0; 133 + } 134 + 135 + .comment { 136 + background: white; 137 + border-radius: 0.5rem; 138 + padding: 1rem; 139 + margin: 1rem 0; 140 + border-left: 3px solid #667eea; 141 + } 142 + 143 + .comment-header { 144 + display: flex; 145 + align-items: center; 146 + gap: 1rem; 147 + margin-bottom: 0.75rem; 148 + } 149 + 150 + .comment-body { 151 + color: #555; 152 + } 153 + 154 + /* Footer */ 155 + footer { 156 + background: #343a40; 157 + color: white; 158 + text-align: center; 159 + padding: 2rem 1rem; 160 + margin-top: 4rem; 161 + } 162 + 163 + /* Custom theme examples */ 164 + .theme-dark { 165 + --bushel-author-background: #2d3748; 166 + --bushel-author-name-color: #e2e8f0; 167 + --bushel-author-title-color: #a0aec0; 168 + --bushel-author-social-color: #4299e1; 169 + --bushel-author-padding: 1rem; 170 + --bushel-author-border-radius: 0.5rem; 171 + } 172 + 173 + .theme-minimal { 174 + --bushel-author-photo-size: 1.5rem; 175 + --bushel-author-gap: 0.5rem; 176 + --bushel-author-name-size: 0.875rem; 177 + --bushel-author-social-size: 0.75rem; 178 + } 179 + 180 + .theme-colorful { 181 + --bushel-author-background: linear-gradient(45deg, #ff6b6b, #4ecdc4); 182 + --bushel-author-name-color: white; 183 + --bushel-author-title-color: rgba(255,255,255,0.8); 184 + --bushel-author-social-color: white; 185 + --bushel-author-padding: 1rem; 186 + --bushel-author-border-radius: 1rem; 187 + } 188 + 189 + @media (max-width: 768px) { 190 + header h1 { 191 + font-size: 2rem; 192 + } 193 + 194 + .section { 195 + margin: 2rem 0; 196 + padding: 1.5rem; 197 + } 198 + 199 + .blog-post .meta { 200 + flex-direction: column; 201 + align-items: flex-start; 202 + gap: 0.5rem; 203 + } 204 + 205 + .article-byline { 206 + flex-direction: column; 207 + align-items: flex-start; 208 + text-align: left; 209 + } 210 + } 211 + </style> 212 + </head> 213 + <body> 214 + <header> 215 + <h1>Bushel Author Components</h1> 216 + <p>Semantic, accessible author information with microformat support for the modern web</p> 217 + </header> 218 + 219 + <main> 220 + <section class="section"> 221 + <h2>Blog Post Authors</h2> 222 + <p>Perfect for article bylines and author attribution in blog posts and news articles.</p> 223 + 224 + <article class="blog-post"> 225 + <h3>Building Accessible Web Components in 2025</h3> 226 + <div class="meta"> 227 + <bushel-author 228 + name="Sarah Chen" 229 + job-title="Senior Frontend Developer" 230 + org="Mozilla" 231 + photo="https://picsum.photos/200/200?random=1" 232 + github="sarahc" 233 + mastodon="https://mastodon.social/@sarahc" 234 + website="https://sarahchen.dev" 235 + link-profile> 236 + </bushel-author> 237 + <span style="color: #666;">Published March 15, 2025</span> 238 + </div> 239 + <div class="content"> 240 + <p>Web components have revolutionized how we build reusable UI elements, but accessibility 241 + often gets overlooked. This comprehensive guide covers everything you need to know about 242 + creating web components that work for everyone...</p> 243 + </div> 244 + </article> 245 + 246 + <article class="blog-post"> 247 + <h3>The Future of Distributed Social Networks</h3> 248 + <div class="meta"> 249 + <bushel-author 250 + name="Dr. Marcus Thompson" 251 + job-title="Research Scientist" 252 + org="Decentralized Web Foundation" 253 + photo="https://picsum.photos/200/200?random=2" 254 + email="marcus@dwf.org" 255 + github="marcust" 256 + bio="Researcher focused on decentralized protocols and social networks"> 257 + </bushel-author> 258 + <span style="color: #666;">Published March 12, 2025</span> 259 + </div> 260 + <div class="content"> 261 + <p>As we move beyond centralized social media platforms, new protocols and technologies 262 + are emerging that put users back in control of their data and social connections...</p> 263 + </div> 264 + </article> 265 + </section> 266 + 267 + <section class="section"> 268 + <h2>Team Pages</h2> 269 + <p>Showcase your team members with rich profile cards that include all their professional information.</p> 270 + 271 + <div class="team-grid"> 272 + <div class="team-member"> 273 + <bushel-author 274 + display-mode="card" 275 + name="Emily Rodriguez" 276 + job-title="Lead UX Designer" 277 + org="DesignCorp" 278 + photo="https://picsum.photos/300/300?random=3" 279 + bio="Creating intuitive experiences that delight users and drive business results." 280 + website="https://emilyrodriguez.design" 281 + github="emilyux" 282 + twitter="emilyux_design" 283 + link-profile> 284 + </bushel-author> 285 + </div> 286 + 287 + <div class="team-member"> 288 + <bushel-author 289 + display-mode="card" 290 + name="Alex Kim" 291 + job-title="DevOps Engineer" 292 + org="DesignCorp" 293 + photo="https://picsum.photos/300/300?random=4" 294 + bio="Building reliable infrastructure and deployment pipelines for modern applications." 295 + github="alexkimops" 296 + mastodon="https://fosstodon.org/@alexkim" 297 + website="https://alexkim.dev"> 298 + </bushel-author> 299 + </div> 300 + 301 + <div class="team-member"> 302 + <bushel-author 303 + display-mode="card" 304 + given-name="Isabella" 305 + family-name="Martinez" 306 + nickname="Izzy" 307 + job-title="Product Manager" 308 + org="DesignCorp" 309 + photo="https://picsum.photos/300/300?random=5" 310 + bio="Bridging the gap between user needs and technical possibilities." 311 + email="izzy@designcorp.com" 312 + github="izzymartinez" 313 + website="https://izzy.pm"> 314 + </bushel-author> 315 + </div> 316 + </div> 317 + </section> 318 + 319 + <section class="section"> 320 + <h2>Article Collaboration</h2> 321 + <p>Show multiple authors and contributors for collaborative content.</p> 322 + 323 + <div class="article-byline"> 324 + <strong>Authors:</strong> 325 + <bushel-author 326 + display-mode="compact" 327 + name="David Park" 328 + job-title="Security Researcher" 329 + photo="https://picsum.photos/200/200?random=6" 330 + github="davidpark"> 331 + </bushel-author> 332 + <bushel-author 333 + display-mode="compact" 334 + name="Lisa Wang" 335 + job-title="Cryptography Expert" 336 + photo="https://picsum.photos/200/200?random=7" 337 + github="lisawang"> 338 + </bushel-author> 339 + </div> 340 + 341 + <div class="article-content"> 342 + <h3>Zero-Knowledge Proofs in Web Applications</h3> 343 + <p>This collaborative research paper explores the practical implementation of zero-knowledge 344 + proofs in modern web applications, covering both theoretical foundations and real-world use cases...</p> 345 + </div> 346 + </section> 347 + 348 + <section class="section"> 349 + <h2>Comment Threads</h2> 350 + <p>Perfect for showing comment authors in discussion threads and forums.</p> 351 + 352 + <div class="comment-thread"> 353 + <h3>Discussion: "Best Practices for Web Component Testing"</h3> 354 + 355 + <div class="comment"> 356 + <div class="comment-header"> 357 + <bushel-author 358 + display-mode="compact" 359 + name="Rachel Green" 360 + photo="https://picsum.photos/200/200?random=8" 361 + github="rachelgreen"> 362 + </bushel-author> 363 + <span style="color: #666; font-size: 0.875rem;">2 hours ago</span> 364 + </div> 365 + <div class="comment-body"> 366 + Great article! I've been struggling with testing shadow DOM interactions. 367 + The approach you outlined for using testing-library with web components is exactly what I needed. 368 + </div> 369 + </div> 370 + 371 + <div class="comment"> 372 + <div class="comment-header"> 373 + <bushel-author 374 + display-mode="compact" 375 + name="James Wilson" 376 + job-title="Test Automation Engineer" 377 + photo="https://picsum.photos/200/200?random=9" 378 + github="jameswilson"> 379 + </bushel-author> 380 + <span style="color: #666; font-size: 0.875rem;">1 hour ago</span> 381 + </div> 382 + <div class="comment-body"> 383 + @rachelgreen Have you tried the new Web Component Testing utilities in Playwright? 384 + They've made shadow DOM testing much more straightforward. 385 + </div> 386 + </div> 387 + 388 + <div class="comment"> 389 + <div class="comment-header"> 390 + <bushel-author 391 + display-mode="compact" 392 + name="Priya Patel" 393 + photo="https://picsum.photos/200/200?random=10" 394 + mastodon="https://mastodon.social/@priya" 395 + github="priyapatel"> 396 + </bushel-author> 397 + <span style="color: #666; font-size: 0.875rem;">30 minutes ago</span> 398 + </div> 399 + <div class="comment-body"> 400 + Thanks for mentioning Playwright! I'll definitely check that out. 401 + We've been using Cypress but shadow DOM support has been limited. 402 + </div> 403 + </div> 404 + </div> 405 + </section> 406 + 407 + <section class="section"> 408 + <h2>Themed Variants</h2> 409 + <p>Customize the appearance using CSS custom properties to match your brand.</p> 410 + 411 + <div style="display: grid; gap: 2rem; margin-top: 2rem;"> 412 + <div style="background: #1a202c; padding: 2rem; border-radius: 0.5rem;"> 413 + <h3 style="color: white; margin-bottom: 1rem;">Dark Theme</h3> 414 + <bushel-author 415 + class="theme-dark" 416 + name="Morgan Foster" 417 + job-title="Night Shift Developer" 418 + photo="https://picsum.photos/200/200?random=11" 419 + github="morganf" 420 + website="https://morganfoster.dev" 421 + link-profile> 422 + </bushel-author> 423 + </div> 424 + 425 + <div style="padding: 1rem; border: 2px dashed #ddd; border-radius: 0.5rem;"> 426 + <h3 style="margin-bottom: 1rem;">Minimal Theme</h3> 427 + <bushel-author 428 + class="theme-minimal" 429 + name="Taylor Swift" 430 + job-title="Minimalist Designer" 431 + photo="https://picsum.photos/200/200?random=12" 432 + github="taylorswift"> 433 + </bushel-author> 434 + </div> 435 + 436 + <div style="padding: 2rem; background: #f0f8ff; border-radius: 0.5rem;"> 437 + <h3 style="margin-bottom: 1rem;">Colorful Theme</h3> 438 + <bushel-author 439 + class="theme-colorful" 440 + name="Jordan Rivers" 441 + job-title="Creative Developer" 442 + photo="https://picsum.photos/200/200?random=13" 443 + github="jordanrivers" 444 + mastodon="https://mastodon.art/@jordan" 445 + website="https://jordanrivers.art" 446 + link-profile> 447 + </bushel-author> 448 + </div> 449 + </div> 450 + </section> 451 + 452 + <section class="section"> 453 + <h2>Technical Implementation</h2> 454 + <p>Each component automatically generates semantic HTML with proper microformat markup:</p> 455 + <ul style="list-style: disc; padding-left: 2rem; color: #666;"> 456 + <li><strong>h-card microformat</strong> - Machine-readable contact information</li> 457 + <li><strong>Semantic HTML</strong> - Proper heading hierarchy and landmark elements</li> 458 + <li><strong>Accessibility</strong> - ARIA labels, keyboard navigation, screen reader support</li> 459 + <li><strong>Progressive Enhancement</strong> - Works without JavaScript, enhanced with it</li> 460 + <li><strong>Responsive Design</strong> - Adapts to all screen sizes and devices</li> 461 + <li><strong>Social Verification</strong> - Includes rel="me" links for identity verification</li> 462 + </ul> 463 + </section> 464 + </main> 465 + 466 + <footer> 467 + <p>Built with ❤️ using Bushel Web Components</p> 468 + <div style="margin-top: 1rem;"> 469 + <bushel-author 470 + display-mode="compact" 471 + name="Bushel Team" 472 + website="https://github.com/avsm/bushel" 473 + github="avsm/bushel"> 474 + </bushel-author> 475 + </div> 476 + </footer> 477 + </body> 478 + </html>