Initial commit — Singular Particular Space v1
Homepage (site/index.html): integration-v14 promoted, Writings section integrated with 33 pieces clustered by type (stories/essays/miscellany), Writings welcome lightbox, content frame at 98% opacity. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
202
skills/algorithmic-art/LICENSE.txt
Normal file
202
skills/algorithmic-art/LICENSE.txt
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
405
skills/algorithmic-art/SKILL.md
Normal file
405
skills/algorithmic-art/SKILL.md
Normal file
@@ -0,0 +1,405 @@
|
||||
---
|
||||
name: algorithmic-art
|
||||
description: Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.
|
||||
license: Complete terms in LICENSE.txt
|
||||
---
|
||||
|
||||
Algorithmic philosophies are computational aesthetic movements that are then expressed through code. Output .md files (philosophy), .html files (interactive viewer), and .js files (generative algorithms).
|
||||
|
||||
This happens in two steps:
|
||||
1. Algorithmic Philosophy Creation (.md file)
|
||||
2. Express by creating p5.js generative art (.html + .js files)
|
||||
|
||||
First, undertake this task:
|
||||
|
||||
## ALGORITHMIC PHILOSOPHY CREATION
|
||||
|
||||
To begin, create an ALGORITHMIC PHILOSOPHY (not static images or templates) that will be interpreted through:
|
||||
- Computational processes, emergent behavior, mathematical beauty
|
||||
- Seeded randomness, noise fields, organic systems
|
||||
- Particles, flows, fields, forces
|
||||
- Parametric variation and controlled chaos
|
||||
|
||||
### THE CRITICAL UNDERSTANDING
|
||||
- What is received: Some subtle input or instructions by the user to take into account, but use as a foundation; it should not constrain creative freedom.
|
||||
- What is created: An algorithmic philosophy/generative aesthetic movement.
|
||||
- What happens next: The same version receives the philosophy and EXPRESSES IT IN CODE - creating p5.js sketches that are 90% algorithmic generation, 10% essential parameters.
|
||||
|
||||
Consider this approach:
|
||||
- Write a manifesto for a generative art movement
|
||||
- The next phase involves writing the algorithm that brings it to life
|
||||
|
||||
The philosophy must emphasize: Algorithmic expression. Emergent behavior. Computational beauty. Seeded variation.
|
||||
|
||||
### HOW TO GENERATE AN ALGORITHMIC PHILOSOPHY
|
||||
|
||||
**Name the movement** (1-2 words): "Organic Turbulence" / "Quantum Harmonics" / "Emergent Stillness"
|
||||
|
||||
**Articulate the philosophy** (4-6 paragraphs - concise but complete):
|
||||
|
||||
To capture the ALGORITHMIC essence, express how this philosophy manifests through:
|
||||
- Computational processes and mathematical relationships?
|
||||
- Noise functions and randomness patterns?
|
||||
- Particle behaviors and field dynamics?
|
||||
- Temporal evolution and system states?
|
||||
- Parametric variation and emergent complexity?
|
||||
|
||||
**CRITICAL GUIDELINES:**
|
||||
- **Avoid redundancy**: Each algorithmic aspect should be mentioned once. Avoid repeating concepts about noise theory, particle dynamics, or mathematical principles unless adding new depth.
|
||||
- **Emphasize craftsmanship REPEATEDLY**: The philosophy MUST stress multiple times that the final algorithm should appear as though it took countless hours to develop, was refined with care, and comes from someone at the absolute top of their field. This framing is essential - repeat phrases like "meticulously crafted algorithm," "the product of deep computational expertise," "painstaking optimization," "master-level implementation."
|
||||
- **Leave creative space**: Be specific about the algorithmic direction, but concise enough that the next Claude has room to make interpretive implementation choices at an extremely high level of craftsmanship.
|
||||
|
||||
The philosophy must guide the next version to express ideas ALGORITHMICALLY, not through static images. Beauty lives in the process, not the final frame.
|
||||
|
||||
### PHILOSOPHY EXAMPLES
|
||||
|
||||
**"Organic Turbulence"**
|
||||
Philosophy: Chaos constrained by natural law, order emerging from disorder.
|
||||
Algorithmic expression: Flow fields driven by layered Perlin noise. Thousands of particles following vector forces, their trails accumulating into organic density maps. Multiple noise octaves create turbulent regions and calm zones. Color emerges from velocity and density - fast particles burn bright, slow ones fade to shadow. The algorithm runs until equilibrium - a meticulously tuned balance where every parameter was refined through countless iterations by a master of computational aesthetics.
|
||||
|
||||
**"Quantum Harmonics"**
|
||||
Philosophy: Discrete entities exhibiting wave-like interference patterns.
|
||||
Algorithmic expression: Particles initialized on a grid, each carrying a phase value that evolves through sine waves. When particles are near, their phases interfere - constructive interference creates bright nodes, destructive creates voids. Simple harmonic motion generates complex emergent mandalas. The result of painstaking frequency calibration where every ratio was carefully chosen to produce resonant beauty.
|
||||
|
||||
**"Recursive Whispers"**
|
||||
Philosophy: Self-similarity across scales, infinite depth in finite space.
|
||||
Algorithmic expression: Branching structures that subdivide recursively. Each branch slightly randomized but constrained by golden ratios. L-systems or recursive subdivision generate tree-like forms that feel both mathematical and organic. Subtle noise perturbations break perfect symmetry. Line weights diminish with each recursion level. Every branching angle the product of deep mathematical exploration.
|
||||
|
||||
**"Field Dynamics"**
|
||||
Philosophy: Invisible forces made visible through their effects on matter.
|
||||
Algorithmic expression: Vector fields constructed from mathematical functions or noise. Particles born at edges, flowing along field lines, dying when they reach equilibrium or boundaries. Multiple fields can attract, repel, or rotate particles. The visualization shows only the traces - ghost-like evidence of invisible forces. A computational dance meticulously choreographed through force balance.
|
||||
|
||||
**"Stochastic Crystallization"**
|
||||
Philosophy: Random processes crystallizing into ordered structures.
|
||||
Algorithmic expression: Randomized circle packing or Voronoi tessellation. Start with random points, let them evolve through relaxation algorithms. Cells push apart until equilibrium. Color based on cell size, neighbor count, or distance from center. The organic tiling that emerges feels both random and inevitable. Every seed produces unique crystalline beauty - the mark of a master-level generative algorithm.
|
||||
|
||||
*These are condensed examples. The actual algorithmic philosophy should be 4-6 substantial paragraphs.*
|
||||
|
||||
### ESSENTIAL PRINCIPLES
|
||||
- **ALGORITHMIC PHILOSOPHY**: Creating a computational worldview to be expressed through code
|
||||
- **PROCESS OVER PRODUCT**: Always emphasize that beauty emerges from the algorithm's execution - each run is unique
|
||||
- **PARAMETRIC EXPRESSION**: Ideas communicate through mathematical relationships, forces, behaviors - not static composition
|
||||
- **ARTISTIC FREEDOM**: The next Claude interprets the philosophy algorithmically - provide creative implementation room
|
||||
- **PURE GENERATIVE ART**: This is about making LIVING ALGORITHMS, not static images with randomness
|
||||
- **EXPERT CRAFTSMANSHIP**: Repeatedly emphasize the final algorithm must feel meticulously crafted, refined through countless iterations, the product of deep expertise by someone at the absolute top of their field in computational aesthetics
|
||||
|
||||
**The algorithmic philosophy should be 4-6 paragraphs long.** Fill it with poetic computational philosophy that brings together the intended vision. Avoid repeating the same points. Output this algorithmic philosophy as a .md file.
|
||||
|
||||
---
|
||||
|
||||
## DEDUCING THE CONCEPTUAL SEED
|
||||
|
||||
**CRITICAL STEP**: Before implementing the algorithm, identify the subtle conceptual thread from the original request.
|
||||
|
||||
**THE ESSENTIAL PRINCIPLE**:
|
||||
The concept is a **subtle, niche reference embedded within the algorithm itself** - not always literal, always sophisticated. Someone familiar with the subject should feel it intuitively, while others simply experience a masterful generative composition. The algorithmic philosophy provides the computational language. The deduced concept provides the soul - the quiet conceptual DNA woven invisibly into parameters, behaviors, and emergence patterns.
|
||||
|
||||
This is **VERY IMPORTANT**: The reference must be so refined that it enhances the work's depth without announcing itself. Think like a jazz musician quoting another song through algorithmic harmony - only those who know will catch it, but everyone appreciates the generative beauty.
|
||||
|
||||
---
|
||||
|
||||
## P5.JS IMPLEMENTATION
|
||||
|
||||
With the philosophy AND conceptual framework established, express it through code. Pause to gather thoughts before proceeding. Use only the algorithmic philosophy created and the instructions below.
|
||||
|
||||
### ⚠️ STEP 0: READ THE TEMPLATE FIRST ⚠️
|
||||
|
||||
**CRITICAL: BEFORE writing any HTML:**
|
||||
|
||||
1. **Read** `templates/viewer.html` using the Read tool
|
||||
2. **Study** the exact structure, styling, and Anthropic branding
|
||||
3. **Use that file as the LITERAL STARTING POINT** - not just inspiration
|
||||
4. **Keep all FIXED sections exactly as shown** (header, sidebar structure, Anthropic colors/fonts, seed controls, action buttons)
|
||||
5. **Replace only the VARIABLE sections** marked in the file's comments (algorithm, parameters, UI controls for parameters)
|
||||
|
||||
**Avoid:**
|
||||
- ❌ Creating HTML from scratch
|
||||
- ❌ Inventing custom styling or color schemes
|
||||
- ❌ Using system fonts or dark themes
|
||||
- ❌ Changing the sidebar structure
|
||||
|
||||
**Follow these practices:**
|
||||
- ✅ Copy the template's exact HTML structure
|
||||
- ✅ Keep Anthropic branding (Poppins/Lora fonts, light colors, gradient backdrop)
|
||||
- ✅ Maintain the sidebar layout (Seed → Parameters → Colors? → Actions)
|
||||
- ✅ Replace only the p5.js algorithm and parameter controls
|
||||
|
||||
The template is the foundation. Build on it, don't rebuild it.
|
||||
|
||||
---
|
||||
|
||||
To create gallery-quality computational art that lives and breathes, use the algorithmic philosophy as the foundation.
|
||||
|
||||
### TECHNICAL REQUIREMENTS
|
||||
|
||||
**Seeded Randomness (Art Blocks Pattern)**:
|
||||
```javascript
|
||||
// ALWAYS use a seed for reproducibility
|
||||
let seed = 12345; // or hash from user input
|
||||
randomSeed(seed);
|
||||
noiseSeed(seed);
|
||||
```
|
||||
|
||||
**Parameter Structure - FOLLOW THE PHILOSOPHY**:
|
||||
|
||||
To establish parameters that emerge naturally from the algorithmic philosophy, consider: "What qualities of this system can be adjusted?"
|
||||
|
||||
```javascript
|
||||
let params = {
|
||||
seed: 12345, // Always include seed for reproducibility
|
||||
// colors
|
||||
// Add parameters that control YOUR algorithm:
|
||||
// - Quantities (how many?)
|
||||
// - Scales (how big? how fast?)
|
||||
// - Probabilities (how likely?)
|
||||
// - Ratios (what proportions?)
|
||||
// - Angles (what direction?)
|
||||
// - Thresholds (when does behavior change?)
|
||||
};
|
||||
```
|
||||
|
||||
**To design effective parameters, focus on the properties the system needs to be tunable rather than thinking in terms of "pattern types".**
|
||||
|
||||
**Core Algorithm - EXPRESS THE PHILOSOPHY**:
|
||||
|
||||
**CRITICAL**: The algorithmic philosophy should dictate what to build.
|
||||
|
||||
To express the philosophy through code, avoid thinking "which pattern should I use?" and instead think "how to express this philosophy through code?"
|
||||
|
||||
If the philosophy is about **organic emergence**, consider using:
|
||||
- Elements that accumulate or grow over time
|
||||
- Random processes constrained by natural rules
|
||||
- Feedback loops and interactions
|
||||
|
||||
If the philosophy is about **mathematical beauty**, consider using:
|
||||
- Geometric relationships and ratios
|
||||
- Trigonometric functions and harmonics
|
||||
- Precise calculations creating unexpected patterns
|
||||
|
||||
If the philosophy is about **controlled chaos**, consider using:
|
||||
- Random variation within strict boundaries
|
||||
- Bifurcation and phase transitions
|
||||
- Order emerging from disorder
|
||||
|
||||
**The algorithm flows from the philosophy, not from a menu of options.**
|
||||
|
||||
To guide the implementation, let the conceptual essence inform creative and original choices. Build something that expresses the vision for this particular request.
|
||||
|
||||
**Canvas Setup**: Standard p5.js structure:
|
||||
```javascript
|
||||
function setup() {
|
||||
createCanvas(1200, 1200);
|
||||
// Initialize your system
|
||||
}
|
||||
|
||||
function draw() {
|
||||
// Your generative algorithm
|
||||
// Can be static (noLoop) or animated
|
||||
}
|
||||
```
|
||||
|
||||
### CRAFTSMANSHIP REQUIREMENTS
|
||||
|
||||
**CRITICAL**: To achieve mastery, create algorithms that feel like they emerged through countless iterations by a master generative artist. Tune every parameter carefully. Ensure every pattern emerges with purpose. This is NOT random noise - this is CONTROLLED CHAOS refined through deep expertise.
|
||||
|
||||
- **Balance**: Complexity without visual noise, order without rigidity
|
||||
- **Color Harmony**: Thoughtful palettes, not random RGB values
|
||||
- **Composition**: Even in randomness, maintain visual hierarchy and flow
|
||||
- **Performance**: Smooth execution, optimized for real-time if animated
|
||||
- **Reproducibility**: Same seed ALWAYS produces identical output
|
||||
|
||||
### OUTPUT FORMAT
|
||||
|
||||
Output:
|
||||
1. **Algorithmic Philosophy** - As markdown or text explaining the generative aesthetic
|
||||
2. **Single HTML Artifact** - Self-contained interactive generative art built from `templates/viewer.html` (see STEP 0 and next section)
|
||||
|
||||
The HTML artifact contains everything: p5.js (from CDN), the algorithm, parameter controls, and UI - all in one file that works immediately in claude.ai artifacts or any browser. Start from the template file, not from scratch.
|
||||
|
||||
---
|
||||
|
||||
## INTERACTIVE ARTIFACT CREATION
|
||||
|
||||
**REMINDER: `templates/viewer.html` should have already been read (see STEP 0). Use that file as the starting point.**
|
||||
|
||||
To allow exploration of the generative art, create a single, self-contained HTML artifact. Ensure this artifact works immediately in claude.ai or any browser - no setup required. Embed everything inline.
|
||||
|
||||
### CRITICAL: WHAT'S FIXED VS VARIABLE
|
||||
|
||||
The `templates/viewer.html` file is the foundation. It contains the exact structure and styling needed.
|
||||
|
||||
**FIXED (always include exactly as shown):**
|
||||
- Layout structure (header, sidebar, main canvas area)
|
||||
- Anthropic branding (UI colors, fonts, gradients)
|
||||
- Seed section in sidebar:
|
||||
- Seed display
|
||||
- Previous/Next buttons
|
||||
- Random button
|
||||
- Jump to seed input + Go button
|
||||
- Actions section in sidebar:
|
||||
- Regenerate button
|
||||
- Reset button
|
||||
|
||||
**VARIABLE (customize for each artwork):**
|
||||
- The entire p5.js algorithm (setup/draw/classes)
|
||||
- The parameters object (define what the art needs)
|
||||
- The Parameters section in sidebar:
|
||||
- Number of parameter controls
|
||||
- Parameter names
|
||||
- Min/max/step values for sliders
|
||||
- Control types (sliders, inputs, etc.)
|
||||
- Colors section (optional):
|
||||
- Some art needs color pickers
|
||||
- Some art might use fixed colors
|
||||
- Some art might be monochrome (no color controls needed)
|
||||
- Decide based on the art's needs
|
||||
|
||||
**Every artwork should have unique parameters and algorithm!** The fixed parts provide consistent UX - everything else expresses the unique vision.
|
||||
|
||||
### REQUIRED FEATURES
|
||||
|
||||
**1. Parameter Controls**
|
||||
- Sliders for numeric parameters (particle count, noise scale, speed, etc.)
|
||||
- Color pickers for palette colors
|
||||
- Real-time updates when parameters change
|
||||
- Reset button to restore defaults
|
||||
|
||||
**2. Seed Navigation**
|
||||
- Display current seed number
|
||||
- "Previous" and "Next" buttons to cycle through seeds
|
||||
- "Random" button for random seed
|
||||
- Input field to jump to specific seed
|
||||
- Generate 100 variations when requested (seeds 1-100)
|
||||
|
||||
**3. Single Artifact Structure**
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<!-- p5.js from CDN - always available -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.7.0/p5.min.js"></script>
|
||||
<style>
|
||||
/* All styling inline - clean, minimal */
|
||||
/* Canvas on top, controls below */
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="canvas-container"></div>
|
||||
<div id="controls">
|
||||
<!-- All parameter controls -->
|
||||
</div>
|
||||
<script>
|
||||
// ALL p5.js code inline here
|
||||
// Parameter objects, classes, functions
|
||||
// setup() and draw()
|
||||
// UI handlers
|
||||
// Everything self-contained
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
**CRITICAL**: This is a single artifact. No external files, no imports (except p5.js CDN). Everything inline.
|
||||
|
||||
**4. Implementation Details - BUILD THE SIDEBAR**
|
||||
|
||||
The sidebar structure:
|
||||
|
||||
**1. Seed (FIXED)** - Always include exactly as shown:
|
||||
- Seed display
|
||||
- Prev/Next/Random/Jump buttons
|
||||
|
||||
**2. Parameters (VARIABLE)** - Create controls for the art:
|
||||
```html
|
||||
<div class="control-group">
|
||||
<label>Parameter Name</label>
|
||||
<input type="range" id="param" min="..." max="..." step="..." value="..." oninput="updateParam('param', this.value)">
|
||||
<span class="value-display" id="param-value">...</span>
|
||||
</div>
|
||||
```
|
||||
Add as many control-group divs as there are parameters.
|
||||
|
||||
**3. Colors (OPTIONAL/VARIABLE)** - Include if the art needs adjustable colors:
|
||||
- Add color pickers if users should control palette
|
||||
- Skip this section if the art uses fixed colors
|
||||
- Skip if the art is monochrome
|
||||
|
||||
**4. Actions (FIXED)** - Always include exactly as shown:
|
||||
- Regenerate button
|
||||
- Reset button
|
||||
- Download PNG button
|
||||
|
||||
**Requirements**:
|
||||
- Seed controls must work (prev/next/random/jump/display)
|
||||
- All parameters must have UI controls
|
||||
- Regenerate, Reset, Download buttons must work
|
||||
- Keep Anthropic branding (UI styling, not art colors)
|
||||
|
||||
### USING THE ARTIFACT
|
||||
|
||||
The HTML artifact works immediately:
|
||||
1. **In claude.ai**: Displayed as an interactive artifact - runs instantly
|
||||
2. **As a file**: Save and open in any browser - no server needed
|
||||
3. **Sharing**: Send the HTML file - it's completely self-contained
|
||||
|
||||
---
|
||||
|
||||
## VARIATIONS & EXPLORATION
|
||||
|
||||
The artifact includes seed navigation by default (prev/next/random buttons), allowing users to explore variations without creating multiple files. If the user wants specific variations highlighted:
|
||||
|
||||
- Include seed presets (buttons for "Variation 1: Seed 42", "Variation 2: Seed 127", etc.)
|
||||
- Add a "Gallery Mode" that shows thumbnails of multiple seeds side-by-side
|
||||
- All within the same single artifact
|
||||
|
||||
This is like creating a series of prints from the same plate - the algorithm is consistent, but each seed reveals different facets of its potential. The interactive nature means users discover their own favorites by exploring the seed space.
|
||||
|
||||
---
|
||||
|
||||
## THE CREATIVE PROCESS
|
||||
|
||||
**User request** → **Algorithmic philosophy** → **Implementation**
|
||||
|
||||
Each request is unique. The process involves:
|
||||
|
||||
1. **Interpret the user's intent** - What aesthetic is being sought?
|
||||
2. **Create an algorithmic philosophy** (4-6 paragraphs) describing the computational approach
|
||||
3. **Implement it in code** - Build the algorithm that expresses this philosophy
|
||||
4. **Design appropriate parameters** - What should be tunable?
|
||||
5. **Build matching UI controls** - Sliders/inputs for those parameters
|
||||
|
||||
**The constants**:
|
||||
- Anthropic branding (colors, fonts, layout)
|
||||
- Seed navigation (always present)
|
||||
- Self-contained HTML artifact
|
||||
|
||||
**Everything else is variable**:
|
||||
- The algorithm itself
|
||||
- The parameters
|
||||
- The UI controls
|
||||
- The visual outcome
|
||||
|
||||
To achieve the best results, trust creativity and let the philosophy guide the implementation.
|
||||
|
||||
---
|
||||
|
||||
## RESOURCES
|
||||
|
||||
This skill includes helpful templates and documentation:
|
||||
|
||||
- **templates/viewer.html**: REQUIRED STARTING POINT for all HTML artifacts.
|
||||
- This is the foundation - contains the exact structure and Anthropic branding
|
||||
- **Keep unchanged**: Layout structure, sidebar organization, Anthropic colors/fonts, seed controls, action buttons
|
||||
- **Replace**: The p5.js algorithm, parameter definitions, and UI controls in Parameters section
|
||||
- The extensive comments in the file mark exactly what to keep vs replace
|
||||
|
||||
- **templates/generator_template.js**: Reference for p5.js best practices and code structure principles.
|
||||
- Shows how to organize parameters, use seeded randomness, structure classes
|
||||
- NOT a pattern menu - use these principles to build unique algorithms
|
||||
- Embed algorithms inline in the HTML artifact (don't create separate .js files)
|
||||
|
||||
**Critical reminder**:
|
||||
- The **template is the STARTING POINT**, not inspiration
|
||||
- The **algorithm is where to create** something unique
|
||||
- Don't copy the flow field example - build what the philosophy demands
|
||||
- But DO keep the exact UI structure and Anthropic branding from the template
|
||||
223
skills/algorithmic-art/templates/generator_template.js
Normal file
223
skills/algorithmic-art/templates/generator_template.js
Normal file
@@ -0,0 +1,223 @@
|
||||
/**
|
||||
* ═══════════════════════════════════════════════════════════════════════════
|
||||
* P5.JS GENERATIVE ART - BEST PRACTICES
|
||||
* ═══════════════════════════════════════════════════════════════════════════
|
||||
*
|
||||
* This file shows STRUCTURE and PRINCIPLES for p5.js generative art.
|
||||
* It does NOT prescribe what art you should create.
|
||||
*
|
||||
* Your algorithmic philosophy should guide what you build.
|
||||
* These are just best practices for how to structure your code.
|
||||
*
|
||||
* ═══════════════════════════════════════════════════════════════════════════
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// 1. PARAMETER ORGANIZATION
|
||||
// ============================================================================
|
||||
// Keep all tunable parameters in one object
|
||||
// This makes it easy to:
|
||||
// - Connect to UI controls
|
||||
// - Reset to defaults
|
||||
// - Serialize/save configurations
|
||||
|
||||
let params = {
|
||||
// Define parameters that match YOUR algorithm
|
||||
// Examples (customize for your art):
|
||||
// - Counts: how many elements (particles, circles, branches, etc.)
|
||||
// - Scales: size, speed, spacing
|
||||
// - Probabilities: likelihood of events
|
||||
// - Angles: rotation, direction
|
||||
// - Colors: palette arrays
|
||||
|
||||
seed: 12345,
|
||||
// define colorPalette as an array -- choose whatever colors you'd like ['#d97757', '#6a9bcc', '#788c5d', '#b0aea5']
|
||||
// Add YOUR parameters here based on your algorithm
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 2. SEEDED RANDOMNESS (Critical for reproducibility)
|
||||
// ============================================================================
|
||||
// ALWAYS use seeded random for Art Blocks-style reproducible output
|
||||
|
||||
function initializeSeed(seed) {
|
||||
randomSeed(seed);
|
||||
noiseSeed(seed);
|
||||
// Now all random() and noise() calls will be deterministic
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 3. P5.JS LIFECYCLE
|
||||
// ============================================================================
|
||||
|
||||
function setup() {
|
||||
createCanvas(800, 800);
|
||||
|
||||
// Initialize seed first
|
||||
initializeSeed(params.seed);
|
||||
|
||||
// Set up your generative system
|
||||
// This is where you initialize:
|
||||
// - Arrays of objects
|
||||
// - Grid structures
|
||||
// - Initial positions
|
||||
// - Starting states
|
||||
|
||||
// For static art: call noLoop() at the end of setup
|
||||
// For animated art: let draw() keep running
|
||||
}
|
||||
|
||||
function draw() {
|
||||
// Option 1: Static generation (runs once, then stops)
|
||||
// - Generate everything in setup()
|
||||
// - Call noLoop() in setup()
|
||||
// - draw() doesn't do much or can be empty
|
||||
|
||||
// Option 2: Animated generation (continuous)
|
||||
// - Update your system each frame
|
||||
// - Common patterns: particle movement, growth, evolution
|
||||
// - Can optionally call noLoop() after N frames
|
||||
|
||||
// Option 3: User-triggered regeneration
|
||||
// - Use noLoop() by default
|
||||
// - Call redraw() when parameters change
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 4. CLASS STRUCTURE (When you need objects)
|
||||
// ============================================================================
|
||||
// Use classes when your algorithm involves multiple entities
|
||||
// Examples: particles, agents, cells, nodes, etc.
|
||||
|
||||
class Entity {
|
||||
constructor() {
|
||||
// Initialize entity properties
|
||||
// Use random() here - it will be seeded
|
||||
}
|
||||
|
||||
update() {
|
||||
// Update entity state
|
||||
// This might involve:
|
||||
// - Physics calculations
|
||||
// - Behavioral rules
|
||||
// - Interactions with neighbors
|
||||
}
|
||||
|
||||
display() {
|
||||
// Render the entity
|
||||
// Keep rendering logic separate from update logic
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 5. PERFORMANCE CONSIDERATIONS
|
||||
// ============================================================================
|
||||
|
||||
// For large numbers of elements:
|
||||
// - Pre-calculate what you can
|
||||
// - Use simple collision detection (spatial hashing if needed)
|
||||
// - Limit expensive operations (sqrt, trig) when possible
|
||||
// - Consider using p5 vectors efficiently
|
||||
|
||||
// For smooth animation:
|
||||
// - Aim for 60fps
|
||||
// - Profile if things are slow
|
||||
// - Consider reducing particle counts or simplifying calculations
|
||||
|
||||
// ============================================================================
|
||||
// 6. UTILITY FUNCTIONS
|
||||
// ============================================================================
|
||||
|
||||
// Color utilities
|
||||
function hexToRgb(hex) {
|
||||
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
||||
return result ? {
|
||||
r: parseInt(result[1], 16),
|
||||
g: parseInt(result[2], 16),
|
||||
b: parseInt(result[3], 16)
|
||||
} : null;
|
||||
}
|
||||
|
||||
function colorFromPalette(index) {
|
||||
return params.colorPalette[index % params.colorPalette.length];
|
||||
}
|
||||
|
||||
// Mapping and easing
|
||||
function mapRange(value, inMin, inMax, outMin, outMax) {
|
||||
return outMin + (outMax - outMin) * ((value - inMin) / (inMax - inMin));
|
||||
}
|
||||
|
||||
function easeInOutCubic(t) {
|
||||
return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
|
||||
}
|
||||
|
||||
// Constrain to bounds
|
||||
function wrapAround(value, max) {
|
||||
if (value < 0) return max;
|
||||
if (value > max) return 0;
|
||||
return value;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 7. PARAMETER UPDATES (Connect to UI)
|
||||
// ============================================================================
|
||||
|
||||
function updateParameter(paramName, value) {
|
||||
params[paramName] = value;
|
||||
// Decide if you need to regenerate or just update
|
||||
// Some params can update in real-time, others need full regeneration
|
||||
}
|
||||
|
||||
function regenerate() {
|
||||
// Reinitialize your generative system
|
||||
// Useful when parameters change significantly
|
||||
initializeSeed(params.seed);
|
||||
// Then regenerate your system
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 8. COMMON P5.JS PATTERNS
|
||||
// ============================================================================
|
||||
|
||||
// Drawing with transparency for trails/fading
|
||||
function fadeBackground(opacity) {
|
||||
fill(250, 249, 245, opacity); // Anthropic light with alpha
|
||||
noStroke();
|
||||
rect(0, 0, width, height);
|
||||
}
|
||||
|
||||
// Using noise for organic variation
|
||||
function getNoiseValue(x, y, scale = 0.01) {
|
||||
return noise(x * scale, y * scale);
|
||||
}
|
||||
|
||||
// Creating vectors from angles
|
||||
function vectorFromAngle(angle, magnitude = 1) {
|
||||
return createVector(cos(angle), sin(angle)).mult(magnitude);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 9. EXPORT FUNCTIONS
|
||||
// ============================================================================
|
||||
|
||||
function exportImage() {
|
||||
saveCanvas('generative-art-' + params.seed, 'png');
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// REMEMBER
|
||||
// ============================================================================
|
||||
//
|
||||
// These are TOOLS and PRINCIPLES, not a recipe.
|
||||
// Your algorithmic philosophy should guide WHAT you create.
|
||||
// This structure helps you create it WELL.
|
||||
//
|
||||
// Focus on:
|
||||
// - Clean, readable code
|
||||
// - Parameterized for exploration
|
||||
// - Seeded for reproducibility
|
||||
// - Performant execution
|
||||
//
|
||||
// The art itself is entirely up to you!
|
||||
//
|
||||
// ============================================================================
|
||||
599
skills/algorithmic-art/templates/viewer.html
Normal file
599
skills/algorithmic-art/templates/viewer.html
Normal file
@@ -0,0 +1,599 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
THIS IS A TEMPLATE THAT SHOULD BE USED EVERY TIME AND MODIFIED.
|
||||
WHAT TO KEEP:
|
||||
✓ Overall structure (header, sidebar, main content)
|
||||
✓ Anthropic branding (colors, fonts, layout)
|
||||
✓ Seed navigation section (always include this)
|
||||
✓ Self-contained artifact (everything inline)
|
||||
|
||||
WHAT TO CREATIVELY EDIT:
|
||||
✗ The p5.js algorithm (implement YOUR vision)
|
||||
✗ The parameters (define what YOUR art needs)
|
||||
✗ The UI controls (match YOUR parameters)
|
||||
|
||||
Let your philosophy guide the implementation.
|
||||
The world is your oyster - be creative!
|
||||
-->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Generative Art Viewer</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.7.0/p5.min.js"></script>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&family=Lora:wght@400;500&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
/* Anthropic Brand Colors */
|
||||
:root {
|
||||
--anthropic-dark: #141413;
|
||||
--anthropic-light: #faf9f5;
|
||||
--anthropic-mid-gray: #b0aea5;
|
||||
--anthropic-light-gray: #e8e6dc;
|
||||
--anthropic-orange: #d97757;
|
||||
--anthropic-blue: #6a9bcc;
|
||||
--anthropic-green: #788c5d;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Poppins', sans-serif;
|
||||
background: linear-gradient(135deg, var(--anthropic-light) 0%, #f5f3ee 100%);
|
||||
min-height: 100vh;
|
||||
color: var(--anthropic-dark);
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
padding: 20px;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
/* Sidebar */
|
||||
.sidebar {
|
||||
width: 320px;
|
||||
flex-shrink: 0;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
padding: 24px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 10px 30px rgba(20, 20, 19, 0.1);
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.sidebar h1 {
|
||||
font-family: 'Lora', serif;
|
||||
font-size: 24px;
|
||||
font-weight: 500;
|
||||
color: var(--anthropic-dark);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.sidebar .subtitle {
|
||||
color: var(--anthropic-mid-gray);
|
||||
font-size: 14px;
|
||||
margin-bottom: 32px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* Control Sections */
|
||||
.control-section {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.control-section h3 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: var(--anthropic-dark);
|
||||
margin-bottom: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.control-section h3::before {
|
||||
content: '•';
|
||||
color: var(--anthropic-orange);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Seed Controls */
|
||||
.seed-input {
|
||||
width: 100%;
|
||||
background: var(--anthropic-light);
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 14px;
|
||||
margin-bottom: 12px;
|
||||
border: 1px solid var(--anthropic-light-gray);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.seed-input:focus {
|
||||
outline: none;
|
||||
border-color: var(--anthropic-orange);
|
||||
box-shadow: 0 0 0 2px rgba(217, 119, 87, 0.1);
|
||||
background: white;
|
||||
}
|
||||
|
||||
.seed-controls {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.regen-button {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Parameter Controls */
|
||||
.control-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.control-group label {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--anthropic-dark);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.slider-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.slider-container input[type="range"] {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
background: var(--anthropic-light-gray);
|
||||
border-radius: 2px;
|
||||
outline: none;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
.slider-container input[type="range"]::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: var(--anthropic-orange);
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.slider-container input[type="range"]::-webkit-slider-thumb:hover {
|
||||
transform: scale(1.1);
|
||||
background: #c86641;
|
||||
}
|
||||
|
||||
.slider-container input[type="range"]::-moz-range-thumb {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: var(--anthropic-orange);
|
||||
border-radius: 50%;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.value-display {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 12px;
|
||||
color: var(--anthropic-mid-gray);
|
||||
min-width: 60px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* Color Pickers */
|
||||
.color-group {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.color-group label {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
color: var(--anthropic-mid-gray);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.color-picker-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.color-picker-container input[type="color"] {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.color-value {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 12px;
|
||||
color: var(--anthropic-mid-gray);
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.button {
|
||||
background: var(--anthropic-orange);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 16px;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background: #c86641;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.button:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.button.secondary {
|
||||
background: var(--anthropic-blue);
|
||||
}
|
||||
|
||||
.button.secondary:hover {
|
||||
background: #5a8bb8;
|
||||
}
|
||||
|
||||
.button.tertiary {
|
||||
background: var(--anthropic-green);
|
||||
}
|
||||
|
||||
.button.tertiary:hover {
|
||||
background: #6b7b52;
|
||||
}
|
||||
|
||||
.button-row {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.button-row .button {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* Canvas Area */
|
||||
.canvas-area {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
#canvas-container {
|
||||
width: 100%;
|
||||
max-width: 1000px;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 20px 40px rgba(20, 20, 19, 0.1);
|
||||
background: white;
|
||||
}
|
||||
|
||||
#canvas-container canvas {
|
||||
display: block;
|
||||
width: 100% !important;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
/* Loading State */
|
||||
.loading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 18px;
|
||||
color: var(--anthropic-mid-gray);
|
||||
}
|
||||
|
||||
/* Responsive - Stack on mobile */
|
||||
@media (max-width: 600px) {
|
||||
.container {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.canvas-area {
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<!-- Control Sidebar -->
|
||||
<div class="sidebar">
|
||||
<!-- Headers (CUSTOMIZE THIS FOR YOUR ART) -->
|
||||
<h1>TITLE - EDIT</h1>
|
||||
<div class="subtitle">SUBHEADER - EDIT</div>
|
||||
|
||||
<!-- Seed Section (ALWAYS KEEP THIS) -->
|
||||
<div class="control-section">
|
||||
<h3>Seed</h3>
|
||||
<input type="number" id="seed-input" class="seed-input" value="12345" onchange="updateSeed()">
|
||||
<div class="seed-controls">
|
||||
<button class="button secondary" onclick="previousSeed()">← Prev</button>
|
||||
<button class="button secondary" onclick="nextSeed()">Next →</button>
|
||||
</div>
|
||||
<button class="button tertiary regen-button" onclick="randomSeedAndUpdate()">↻ Random</button>
|
||||
</div>
|
||||
|
||||
<!-- Parameters Section (CUSTOMIZE THIS FOR YOUR ART) -->
|
||||
<div class="control-section">
|
||||
<h3>Parameters</h3>
|
||||
|
||||
<!-- Particle Count -->
|
||||
<div class="control-group">
|
||||
<label>Particle Count</label>
|
||||
<div class="slider-container">
|
||||
<input type="range" id="particleCount" min="1000" max="10000" step="500" value="5000" oninput="updateParam('particleCount', this.value)">
|
||||
<span class="value-display" id="particleCount-value">5000</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Flow Speed -->
|
||||
<div class="control-group">
|
||||
<label>Flow Speed</label>
|
||||
<div class="slider-container">
|
||||
<input type="range" id="flowSpeed" min="0.1" max="2.0" step="0.1" value="0.5" oninput="updateParam('flowSpeed', this.value)">
|
||||
<span class="value-display" id="flowSpeed-value">0.5</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Noise Scale -->
|
||||
<div class="control-group">
|
||||
<label>Noise Scale</label>
|
||||
<div class="slider-container">
|
||||
<input type="range" id="noiseScale" min="0.001" max="0.02" step="0.001" value="0.005" oninput="updateParam('noiseScale', this.value)">
|
||||
<span class="value-display" id="noiseScale-value">0.005</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Trail Length -->
|
||||
<div class="control-group">
|
||||
<label>Trail Length</label>
|
||||
<div class="slider-container">
|
||||
<input type="range" id="trailLength" min="2" max="20" step="1" value="8" oninput="updateParam('trailLength', this.value)">
|
||||
<span class="value-display" id="trailLength-value">8</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Colors Section (OPTIONAL - CUSTOMIZE OR REMOVE) -->
|
||||
<div class="control-section">
|
||||
<h3>Colors</h3>
|
||||
|
||||
<!-- Color 1 -->
|
||||
<div class="color-group">
|
||||
<label>Primary Color</label>
|
||||
<div class="color-picker-container">
|
||||
<input type="color" id="color1" value="#d97757" onchange="updateColor('color1', this.value)">
|
||||
<span class="color-value" id="color1-value">#d97757</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Color 2 -->
|
||||
<div class="color-group">
|
||||
<label>Secondary Color</label>
|
||||
<div class="color-picker-container">
|
||||
<input type="color" id="color2" value="#6a9bcc" onchange="updateColor('color2', this.value)">
|
||||
<span class="color-value" id="color2-value">#6a9bcc</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Color 3 -->
|
||||
<div class="color-group">
|
||||
<label>Accent Color</label>
|
||||
<div class="color-picker-container">
|
||||
<input type="color" id="color3" value="#788c5d" onchange="updateColor('color3', this.value)">
|
||||
<span class="color-value" id="color3-value">#788c5d</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Actions Section (ALWAYS KEEP THIS) -->
|
||||
<div class="control-section">
|
||||
<h3>Actions</h3>
|
||||
<div class="button-row">
|
||||
<button class="button" onclick="resetParameters()">Reset</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Canvas Area -->
|
||||
<div class="canvas-area">
|
||||
<div id="canvas-container">
|
||||
<div class="loading">Initializing generative art...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// GENERATIVE ART PARAMETERS - CUSTOMIZE FOR YOUR ALGORITHM
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
let params = {
|
||||
seed: 12345,
|
||||
particleCount: 5000,
|
||||
flowSpeed: 0.5,
|
||||
noiseScale: 0.005,
|
||||
trailLength: 8,
|
||||
colorPalette: ['#d97757', '#6a9bcc', '#788c5d']
|
||||
};
|
||||
|
||||
let defaultParams = {...params}; // Store defaults for reset
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// P5.JS GENERATIVE ART ALGORITHM - REPLACE WITH YOUR VISION
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
let particles = [];
|
||||
let flowField = [];
|
||||
let cols, rows;
|
||||
let scl = 10; // Flow field resolution
|
||||
|
||||
function setup() {
|
||||
let canvas = createCanvas(1200, 1200);
|
||||
canvas.parent('canvas-container');
|
||||
|
||||
initializeSystem();
|
||||
|
||||
// Remove loading message
|
||||
document.querySelector('.loading').style.display = 'none';
|
||||
}
|
||||
|
||||
function initializeSystem() {
|
||||
// Seed the randomness for reproducibility
|
||||
randomSeed(params.seed);
|
||||
noiseSeed(params.seed);
|
||||
|
||||
// Clear particles and recreate
|
||||
particles = [];
|
||||
|
||||
// Initialize particles
|
||||
for (let i = 0; i < params.particleCount; i++) {
|
||||
particles.push(new Particle());
|
||||
}
|
||||
|
||||
// Calculate flow field dimensions
|
||||
cols = floor(width / scl);
|
||||
rows = floor(height / scl);
|
||||
|
||||
// Generate flow field
|
||||
generateFlowField();
|
||||
|
||||
// Clear background
|
||||
background(250, 249, 245); // Anthropic light background
|
||||
}
|
||||
|
||||
function generateFlowField() {
|
||||
// fill this in
|
||||
}
|
||||
|
||||
function draw() {
|
||||
// fill this in
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// PARTICLE SYSTEM - CUSTOMIZE FOR YOUR ALGORITHM
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
class Particle {
|
||||
constructor() {
|
||||
// fill this in
|
||||
}
|
||||
// fill this in
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// UI CONTROL HANDLERS - CUSTOMIZE FOR YOUR PARAMETERS
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
function updateParam(paramName, value) {
|
||||
// fill this in
|
||||
}
|
||||
|
||||
function updateColor(colorId, value) {
|
||||
// fill this in
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// SEED CONTROL FUNCTIONS - ALWAYS KEEP THESE
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
function updateSeedDisplay() {
|
||||
document.getElementById('seed-input').value = params.seed;
|
||||
}
|
||||
|
||||
function updateSeed() {
|
||||
let input = document.getElementById('seed-input');
|
||||
let newSeed = parseInt(input.value);
|
||||
if (newSeed && newSeed > 0) {
|
||||
params.seed = newSeed;
|
||||
initializeSystem();
|
||||
} else {
|
||||
// Reset to current seed if invalid
|
||||
updateSeedDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
function previousSeed() {
|
||||
params.seed = Math.max(1, params.seed - 1);
|
||||
updateSeedDisplay();
|
||||
initializeSystem();
|
||||
}
|
||||
|
||||
function nextSeed() {
|
||||
params.seed = params.seed + 1;
|
||||
updateSeedDisplay();
|
||||
initializeSystem();
|
||||
}
|
||||
|
||||
function randomSeedAndUpdate() {
|
||||
params.seed = Math.floor(Math.random() * 999999) + 1;
|
||||
updateSeedDisplay();
|
||||
initializeSystem();
|
||||
}
|
||||
|
||||
function resetParameters() {
|
||||
params = {...defaultParams};
|
||||
|
||||
// Update UI elements
|
||||
document.getElementById('particleCount').value = params.particleCount;
|
||||
document.getElementById('particleCount-value').textContent = params.particleCount;
|
||||
document.getElementById('flowSpeed').value = params.flowSpeed;
|
||||
document.getElementById('flowSpeed-value').textContent = params.flowSpeed;
|
||||
document.getElementById('noiseScale').value = params.noiseScale;
|
||||
document.getElementById('noiseScale-value').textContent = params.noiseScale;
|
||||
document.getElementById('trailLength').value = params.trailLength;
|
||||
document.getElementById('trailLength-value').textContent = params.trailLength;
|
||||
|
||||
// Reset colors
|
||||
document.getElementById('color1').value = params.colorPalette[0];
|
||||
document.getElementById('color1-value').textContent = params.colorPalette[0];
|
||||
document.getElementById('color2').value = params.colorPalette[1];
|
||||
document.getElementById('color2-value').textContent = params.colorPalette[1];
|
||||
document.getElementById('color3').value = params.colorPalette[2];
|
||||
document.getElementById('color3-value').textContent = params.colorPalette[2];
|
||||
|
||||
updateSeedDisplay();
|
||||
initializeSystem();
|
||||
}
|
||||
|
||||
// Initialize UI on load
|
||||
window.addEventListener('load', function() {
|
||||
updateSeedDisplay();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
45
skills/annotated-writing/SKILL.md
Normal file
45
skills/annotated-writing/SKILL.md
Normal file
@@ -0,0 +1,45 @@
|
||||
---
|
||||
name: annotated-writing
|
||||
description: >
|
||||
Use this skill when the user says "make a content map" for any piece of writing,
|
||||
OR when the agent receives a completed content map file as input. Two distinct tasks:
|
||||
(1) content mapping: read source text, produce structured annotation data;
|
||||
(2) building: receive content map, produce single-file interactive HTML.
|
||||
These are separate agent tasks. Never attempt both in one context.
|
||||
---
|
||||
|
||||
## This Skill Has Two Modes
|
||||
|
||||
**Mode 1 — Content Mapping** is triggered by: "make a content map"
|
||||
Read `content-mapping-fiction.md` or `content-mapping-non-fiction.md` depending on source type. Produce a `.md` content map file as output.
|
||||
|
||||
**Mode 2 — Build** is triggered by: receiving a content map `.md` file as input.
|
||||
Read `annotated-writing-build.md`. Produce a single-file `.html` artefact as output.
|
||||
|
||||
Never conflate the two. A content-mapping agent should not write HTML. A build agent should not perform literary analysis.
|
||||
|
||||
## Files in This Skill
|
||||
|
||||
| File | Used By | Purpose |
|
||||
|---|---|---|
|
||||
| `content-mapping-fiction.md` | Mapping agent | How to map fiction and historically-situated writing |
|
||||
| `content-mapping-non-fiction.md` | Mapping agent | How to map essays, journalism, documents |
|
||||
| `annotated-writing-build.md` | Build agent | Component library, TOON parsing, HTML output rules |
|
||||
| `SKILL.md` (this file) | Both agents | Entry point, routing |
|
||||
|
||||
## Routing Logic
|
||||
|
||||
```
|
||||
input received
|
||||
│
|
||||
├─ source text .md
|
||||
│ └─ is source fictional? → content-mapping-fiction.md
|
||||
│ is source non-fiction? → content-mapping-non-fiction.md
|
||||
│
|
||||
├─ "make a content map" + source text
|
||||
│ └─ is source fictional? → content-mapping-fiction.md
|
||||
│ is source non-fiction? → content-mapping-non-fiction.md
|
||||
│
|
||||
└─ content map .md file received
|
||||
└─ annotated-writing-build.md
|
||||
```
|
||||
297
skills/annotated-writing/annotated-writing-build.md
Normal file
297
skills/annotated-writing/annotated-writing-build.md
Normal file
@@ -0,0 +1,297 @@
|
||||
# Annotated Writing Build
|
||||
|
||||
Instructions for a build agent. Input: a completed content map `.md` file. Output: a single-file `.html` artefact. Do not perform literary analysis. Do not rewrite or improve the source text. Execute the map.
|
||||
|
||||
---
|
||||
|
||||
## Two-Agent Protocol
|
||||
|
||||
This agent receives a content map. It does not produce content maps. If asked to do both in one context, refuse the analysis task and request the content map as input.
|
||||
|
||||
**What the build agent reads from the content map:**
|
||||
- Tab definitions (TOON)
|
||||
- Decoder metadata (TOON) + decoder bodies (YAML)
|
||||
- Lightbox definitions (YAML)
|
||||
- Accordion content (YAML)
|
||||
- Bias notes (YAML)
|
||||
- Further reading (TOON)
|
||||
- Annotated source text (with `[DECODER]`, `[LIGHTBOX]`, `[ORDER]`, `[LETTER-*]` markers)
|
||||
|
||||
---
|
||||
|
||||
## Reading TOON in the Content Map
|
||||
|
||||
Content maps use TOON for uniform arrays. Parse as follows:
|
||||
|
||||
**Tabular array syntax:**
|
||||
```toon
|
||||
arrayName[N]{field1,field2,field3}:
|
||||
value1,value2,value3
|
||||
value1,value2,value3
|
||||
```
|
||||
`[N]` is the declared row count — validate against actual rows. `{fields}` is the column header — same for every row. Values follow CSV quoting rules: strings containing commas are double-quoted.
|
||||
|
||||
**YAML scalar syntax** (for body text — not TOON):
|
||||
```yaml
|
||||
key: >
|
||||
Block scalar. Paragraph text. May contain commas freely.
|
||||
Blank line = new paragraph in rendered output.
|
||||
```
|
||||
|
||||
Both may appear in the same content map. TOON for metadata; YAML for prose.
|
||||
|
||||
---
|
||||
|
||||
## Aesthetic Direction
|
||||
|
||||
The aesthetic is set per-project by the user. The content map may include an `aesthetic:` key. If absent, infer from context clues in the source text. When in doubt, ask before building.
|
||||
|
||||
Regardless of aesthetic, these rules apply:
|
||||
- Choose fonts that are distinctive and specific to the aesthetic. Never: Arial, Inter, Roboto, system-ui.
|
||||
- Use CSS custom properties for all colors. No hardcoded hex values in component CSS.
|
||||
- Dark or light themes are both valid. Commit to one — do not default to white-on-grey.
|
||||
- Background texture adds atmosphere. Scanlines, grain, parchment patterns, noise — pick one and use it subtly.
|
||||
- Tab navigation is sticky. One tab visible at a time via JS class toggle.
|
||||
|
||||
---
|
||||
|
||||
## Component Library
|
||||
|
||||
All components must be implemented as specified. Do not substitute or simplify.
|
||||
|
||||
---
|
||||
|
||||
### Decoder
|
||||
|
||||
Inline interactive element. Wraps a phrase in prose. Floating panel on click.
|
||||
|
||||
**Critical structural rule:** Every element inside a decoder must be a `<span>`, never a `<div>`. Decoders live inside `<p>` tags. A `<div>` inside a `<p>` silently ejects the panel from the DOM. Use `<span>` with `display: block` for block-like rendering.
|
||||
|
||||
```html
|
||||
<span class="dc" id="[id]">
|
||||
<button class="dc-btn [color-class]" onclick="tog('[id]',this)">[phrase from text]</button>
|
||||
<span class="dc-panel [color-class]">
|
||||
<span class="d-tag [color-class]">[tag text]</span>
|
||||
<span class="d-head">[label]</span>
|
||||
[body text]
|
||||
<a class="d-link [color-class]" href="[url]" target="_blank">→ [link label]</a>
|
||||
</span>
|
||||
</span>
|
||||
```
|
||||
|
||||
**Required CSS:**
|
||||
```css
|
||||
.dc { display: inline; position: relative; }
|
||||
.dc-panel {
|
||||
display: none;
|
||||
position: absolute; /* never relative */
|
||||
top: calc(100% + 6px);
|
||||
left: 0;
|
||||
z-index: 500;
|
||||
width: 300px; /* adjust to aesthetic */
|
||||
}
|
||||
.dc-panel.open { display: block; }
|
||||
.dc-panel.flip { left: auto; right: 0; }
|
||||
```
|
||||
|
||||
**Required JS:**
|
||||
```js
|
||||
function tog(id, btn) {
|
||||
const w = document.getElementById(id);
|
||||
const p = w.querySelector('.dc-panel');
|
||||
const open = p.classList.contains('open');
|
||||
document.querySelectorAll('.dc-panel.open').forEach(x => x.classList.remove('open'));
|
||||
document.querySelectorAll('.dc-btn.open').forEach(x => x.classList.remove('open'));
|
||||
if (!open) {
|
||||
p.classList.add('open'); btn.classList.add('open');
|
||||
const r = p.getBoundingClientRect();
|
||||
p.classList.toggle('flip', r.right > window.innerWidth - 16);
|
||||
}
|
||||
}
|
||||
document.addEventListener('click', e => {
|
||||
if (!e.target.closest('.dc')) {
|
||||
document.querySelectorAll('.dc-panel.open').forEach(x => x.classList.remove('open'));
|
||||
document.querySelectorAll('.dc-btn.open').forEach(x => x.classList.remove('open'));
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
**Trigger visual requirements:**
|
||||
- Must look clickable (cursor pointer, border-bottom dashed or dotted)
|
||||
- Must indicate open/closed state (chevron via `::after`, or similar)
|
||||
- Color coding must match the scheme defined in the content map
|
||||
|
||||
---
|
||||
|
||||
### Lightbox
|
||||
|
||||
Full-screen overlay. Triggered by an inline button in the prose.
|
||||
|
||||
```html
|
||||
<!-- Trigger — inline in prose -->
|
||||
<button class="lb-t [color-class]" onclick="lb('[id]')">[trigger phrase]</button>
|
||||
|
||||
<!-- Lightbox — at end of body, before script tag -->
|
||||
<div class="lb-overlay" id="[id]" onclick="closeLbOv(event,'[id]')">
|
||||
<div class="lb-box [color-class]">
|
||||
<div class="lb-head [color-class]">
|
||||
<div>
|
||||
<span class="lb-eyebrow [color-class]">[eyebrow]</span>
|
||||
<span class="lb-title">[title]</span>
|
||||
</div>
|
||||
<button class="lb-close" onclick="closeLb('[id]')">✕</button>
|
||||
</div>
|
||||
<div class="lb-body">
|
||||
<!-- h3 per section, p per paragraph, lb-src link at end -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
**Required CSS:**
|
||||
```css
|
||||
.lb-overlay { display: none; position: fixed; inset: 0; z-index: 2000;
|
||||
align-items: center; justify-content: center; padding: 20px; }
|
||||
.lb-overlay.open { display: flex; }
|
||||
.lb-box { max-height: 88vh; overflow-y: auto; }
|
||||
.lb-head { position: sticky; top: 0; }
|
||||
```
|
||||
|
||||
**Required JS:**
|
||||
```js
|
||||
function lb(id) { document.getElementById(id).classList.add('open'); document.body.style.overflow = 'hidden'; }
|
||||
function closeLb(id) { document.getElementById(id).classList.remove('open'); document.body.style.overflow = ''; }
|
||||
function closeLbOv(e, id) { if (e.target === document.getElementById(id)) closeLb(id); }
|
||||
document.addEventListener('keydown', e => {
|
||||
if (e.key === 'Escape') {
|
||||
document.querySelectorAll('.lb-overlay.open').forEach(x => x.classList.remove('open'));
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Accordion
|
||||
|
||||
Expandable sections within educational tabs. One open at a time.
|
||||
|
||||
The accordion panel uses `<div>` — it is not inside prose, so block elements are safe here.
|
||||
|
||||
```html
|
||||
<div class="acc">
|
||||
<button class="acc-btn" onclick="acc(this)">
|
||||
<span>[heading]</span>
|
||||
<span class="acc-arrow [color-class]">▾</span>
|
||||
</button>
|
||||
<div class="acc-panel">
|
||||
<!-- p tags per paragraph -->
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
**Required JS:**
|
||||
```js
|
||||
function acc(btn) {
|
||||
const p = btn.nextElementSibling;
|
||||
const open = btn.classList.contains('open');
|
||||
document.querySelectorAll('.acc-btn.open').forEach(b => {
|
||||
b.classList.remove('open'); b.nextElementSibling.classList.remove('open');
|
||||
});
|
||||
if (!open) { btn.classList.add('open'); p.classList.add('open'); }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Bias Note
|
||||
|
||||
A styled callout block at the top of each analytical tab, before the first accordion.
|
||||
|
||||
```html
|
||||
<div class="bias [color-class]">
|
||||
<strong>A note:</strong> [bias note text from content map]
|
||||
</div>
|
||||
```
|
||||
|
||||
Not collapsible. Always visible. One per analytical tab.
|
||||
|
||||
---
|
||||
|
||||
### Tab Navigation
|
||||
|
||||
```html
|
||||
<nav>
|
||||
<button class="active [color-class]" onclick="go('[id]',this,'[color]')">[label]</button>
|
||||
<!-- one per tab -->
|
||||
</nav>
|
||||
```
|
||||
|
||||
```js
|
||||
function go(id, btn, col) {
|
||||
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
||||
document.querySelectorAll('nav button').forEach(b => { b.classList.remove('active'); /* remove all color classes */ });
|
||||
document.getElementById('tab-' + id).classList.add('active');
|
||||
btn.classList.add('active', col || 'white');
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
```
|
||||
|
||||
Tab content divs:
|
||||
```html
|
||||
<div class="tab [active-on-load]" id="tab-[id]">
|
||||
<!-- tab content -->
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Source Text — Markers to HTML
|
||||
|
||||
Translate content map markers into HTML components:
|
||||
|
||||
| Marker | Renders as |
|
||||
|---|---|
|
||||
| `[DECODER:id] phrase [/DECODER]` | `<span class="dc" id="id">` decoder component |
|
||||
| `[LIGHTBOX:id] phrase [/LIGHTBOX]` | `<button class="lb-t">` trigger |
|
||||
| `[ORDER] "text" [/ORDER]` | Styled display block — dialogue/order separator |
|
||||
| `[LETTER-START:sender]` | Opening of styled letter card |
|
||||
| `[LETTER-END]` | Close of letter card |
|
||||
| `[PULL-QUOTE] text [/PULL-QUOTE]` | Styled pull-quote block |
|
||||
| `[EDITORIAL] text [/EDITORIAL]` | Styled editorial aside |
|
||||
| `[SECTION-BREAK]` | `<hr>` with aesthetic treatment |
|
||||
|
||||
Letter senders each get a distinct visual treatment (border color, label, optional stamp). Define sender colors in CSS variables using the project color scheme.
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
|
||||
Single `.html` file. All CSS in `<style>` in `<head>`. All JS in `<script>` immediately before `</body>`. No external dependencies except Google Fonts (loaded via `<link>`). All lightboxes placed together at the end of `<body>`, before `<script>`. All external links use `target="_blank"`.
|
||||
|
||||
---
|
||||
|
||||
## Critical Bugs (Permanent Reference)
|
||||
|
||||
**1. Block elements inside `<p>` tags silently break the DOM.**
|
||||
`<div>` inside `<p>` is invalid HTML. The browser auto-closes the `<p>`, ejecting the decoder panel. Every decoder element must be a `<span>`.
|
||||
|
||||
**2. Decoder panel must be `position: absolute`.**
|
||||
`position: relative` expands the parent span when the panel opens, collapsing the surrounding text. Use `position: absolute`.
|
||||
|
||||
**3. Decoder panels clip off the right edge.**
|
||||
After opening, check `panel.getBoundingClientRect().right > window.innerWidth - 16`. If true, add `.flip` class (`left: auto; right: 0`).
|
||||
|
||||
**4. Lightboxes placed inside tab content divs break scroll.**
|
||||
Place all lightbox overlays as direct children of `<body>`, after all tab content, before `<script>`.
|
||||
|
||||
---
|
||||
|
||||
## Output Requirements
|
||||
|
||||
- Single `.html` file
|
||||
- Source text reproduced exactly — no edits, no corrections (errors in the original stay)
|
||||
- All decoders, lightboxes, accordions, and tabs from the content map implemented
|
||||
- Bias notes present on every analytical tab
|
||||
- All external links open in new tab
|
||||
- Passes a basic sanity check: all decoder IDs referenced in prose exist in the JS/HTML
|
||||
232
skills/annotated-writing/content-mapping-fiction.md
Normal file
232
skills/annotated-writing/content-mapping-fiction.md
Normal file
@@ -0,0 +1,232 @@
|
||||
# Content Mapping: Fiction
|
||||
|
||||
A template for producing structured annotation data from fiction and historically-situated narrative writing. Output is a `.md` content map file consumed by a build agent. Do not write HTML here.
|
||||
|
||||
---
|
||||
|
||||
## Step 0 — Classify the Source
|
||||
|
||||
Before mapping, determine:
|
||||
|
||||
- **Historically situated?** Story uses real events, places, dates, institutions → accuracy-checking is required alongside literary analysis.
|
||||
- **Pure fiction?** No historical anchors → analytical lenses only.
|
||||
- **Challenge/constraint fiction?** Written to a brief → note the constraints; they affect how you read intentionality.
|
||||
|
||||
---
|
||||
|
||||
## Step 1 — Read the Source Completely
|
||||
|
||||
Do not begin mapping until you have read the full text. Identify:
|
||||
|
||||
1. **The surface story** — what literally happens
|
||||
2. **The underneath story** — what the text is actually about (often different)
|
||||
3. **The narrator's position** — first person? Reliable? What do they not notice about themselves?
|
||||
4. **Historical claims** — any named events, dates, places, institutions, objects, honours, or technologies that can be fact-checked
|
||||
5. **Language patterns** — naming conventions, who gets interiority, who gets appearance, whose grief is legitimate
|
||||
|
||||
---
|
||||
|
||||
## Step 2 — Choose Analytical Lenses
|
||||
|
||||
Fiction supports multiple valid readings. Choose 2–4 lenses that the text genuinely rewards. Do not force a lens onto text that doesn't earn it.
|
||||
|
||||
**Available lenses (not exhaustive):**
|
||||
|
||||
| Lens | Apply when... |
|
||||
|---|---|
|
||||
| Unreliable narrator | First-person narration; gap between what narrator claims and what text shows |
|
||||
| Male gaze / naming | Women defined by appearance or social function; asymmetric interiority |
|
||||
| Class position | Character claims outsider status but participates in the system they critique |
|
||||
| Historical accuracy | Text makes factual claims about real events, objects, or honours |
|
||||
| Epistolary / form | Story told through letters, documents — what the form conceals matters |
|
||||
| The unseen character | A character who drives the plot but never speaks or is never named |
|
||||
| Constraint analysis | Challenge fiction — how well are required elements absorbed vs engineered |
|
||||
|
||||
For each chosen lens, note: *what specific passages earn this reading?* If you can't answer, drop the lens.
|
||||
|
||||
---
|
||||
|
||||
## Step 3 — Map Annotation Components
|
||||
|
||||
### 3a. Decoders
|
||||
|
||||
Inline interactive elements. Applied to a specific phrase in the prose. Appear on click as a floating panel.
|
||||
|
||||
**Apply a decoder when:**
|
||||
- A phrase needs factual unpacking (historical event, named place, military honour)
|
||||
- A phrase is a pivot point in the narrative that the reader might miss
|
||||
- A character classification is introduced (naming systems, taxonomy)
|
||||
- A contradiction opens between what the narrator says and what they show
|
||||
- A spelling or factual error exists in the source text
|
||||
|
||||
**Decoder metadata — use TOON for the structured fields:**
|
||||
|
||||
```toon
|
||||
decoders[N]{id,phrase,color,tag,label}:
|
||||
dec-[slug],"exact phrase from text",[default|pink|cyan|amber|red],Tag Text,Panel Heading
|
||||
```
|
||||
|
||||
**Decoder bodies — use YAML for body text (contains commas, complex content):**
|
||||
|
||||
```yaml
|
||||
decoder_bodies:
|
||||
dec-[slug]: >
|
||||
2–4 sentences. State the fact, the contradiction, or the lens observation.
|
||||
Be direct. No hedging within the panel itself — hedging belongs in bias notes.
|
||||
dec-[slug]-link: https://...
|
||||
```
|
||||
|
||||
**Color convention** (establish per-project, document here):
|
||||
- Assign one color per analytical lens or content type
|
||||
- Be consistent: if pink = unreliable narrator catches, every narrator catch is pink
|
||||
- Red = factual error or significant historical inaccuracy
|
||||
|
||||
---
|
||||
|
||||
### 3b. Lightboxes
|
||||
|
||||
Full-screen overlay panels. Triggered from inline text. Use when a topic is too large for a decoder (needs multiple sections, a timeline, or extended context).
|
||||
|
||||
**Apply a lightbox when:**
|
||||
- A decoder body would exceed 5 sentences
|
||||
- The topic has meaningful sub-sections (what it is / what the text gets right / what came after)
|
||||
- The text assumes reader knowledge of something substantial (a war, a legal concept, a cultural tradition)
|
||||
|
||||
```yaml
|
||||
lightboxes:
|
||||
lb-[slug]:
|
||||
eyebrow: Category label (e.g. "Historical Context")
|
||||
title: Lightbox heading
|
||||
color: cyan | amber | default
|
||||
sections:
|
||||
- heading: Section heading
|
||||
body: >
|
||||
Paragraph text.
|
||||
- heading: Section heading
|
||||
body: >
|
||||
Paragraph text.
|
||||
source_url: https://...
|
||||
source_label: Link label text
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3c. Accordions
|
||||
|
||||
Expandable sections within educational tabs. One open at a time.
|
||||
|
||||
```yaml
|
||||
accordions:
|
||||
tab-[tab-id]:
|
||||
- heading: Question or section title
|
||||
body: >
|
||||
Extended prose. Can be multiple paragraphs. Separate with blank lines.
|
||||
**Bold** for emphasis. No decoders inside accordions.
|
||||
- heading: ...
|
||||
body: >
|
||||
...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3d. Tab Architecture
|
||||
|
||||
Four tabs is the default. Rename to fit the content.
|
||||
|
||||
```toon
|
||||
tabs[4]{id,label,color,purpose}:
|
||||
story,"The Story",white,Full source text with inline decoders and lightbox triggers
|
||||
[lens-1-id],[Lens 1 Name],[color],Educational deep-dive on first analytical lens
|
||||
[lens-2-id],[Lens 2 Name],[color],Educational deep-dive on second analytical lens
|
||||
further,"Further Reading",white,Curated external links
|
||||
```
|
||||
|
||||
Add a fifth tab only if the text genuinely requires it. Three lenses in three tabs is acceptable if all three are earned.
|
||||
|
||||
---
|
||||
|
||||
### 3e. Further Reading
|
||||
|
||||
```toon
|
||||
further_reading[N]{group,title,url,desc,color}:
|
||||
"Lens 1","Link Title","https://...","One-line description",default
|
||||
"Lens 1","Link Title","https://...","One-line description",default
|
||||
"Lens 2","Link Title","https://...","One-line description",cyan
|
||||
```
|
||||
|
||||
Groups correspond to tabs. Use the same color as the tab they belong to.
|
||||
|
||||
---
|
||||
|
||||
## Step 4 — Bias Notes
|
||||
|
||||
Every analytical tab requires one bias note. Placed at the top of the tab, before the accordions.
|
||||
|
||||
Rules:
|
||||
- One sentence acknowledging the limitation. Then stop.
|
||||
- State the specific bias, not a generic disclaimer.
|
||||
- If you have a preference for the narrator or a character, say so — it shapes the analysis.
|
||||
- Do not write "I am an AI" — write what the actual bias is.
|
||||
|
||||
```yaml
|
||||
bias_notes:
|
||||
tab-[lens-1-id]: >
|
||||
One sentence. Specific bias. What it shapes in the analysis below.
|
||||
tab-[lens-2-id]: >
|
||||
One sentence. Specific bias.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 5 — Historical Accuracy (if applicable)
|
||||
|
||||
If the source text makes historical claims, produce an accuracy table before the decoder map.
|
||||
|
||||
```toon
|
||||
historical_claims[N]{claim,verdict,detail}:
|
||||
"Claim as stated in text",accurate,"Supporting detail or correction"
|
||||
"Claim as stated in text",inaccurate,"What is actually true; what error was made"
|
||||
"Claim as stated in text",plausible,"Consistent with the period; unverifiable at this level of detail"
|
||||
"Claim as stated in text",anachronism,"Object/concept/institution did not exist at the stated time"
|
||||
```
|
||||
|
||||
Inaccurate claims must become decoder annotations in the source text. Use `color: red`.
|
||||
|
||||
---
|
||||
|
||||
## Step 6 — The Story Section
|
||||
|
||||
Format the source text for the build agent. The text is sacred — reproduce it exactly including original spelling errors. Mark decoder and lightbox trigger points.
|
||||
|
||||
Use this notation inline in the prose block:
|
||||
|
||||
```
|
||||
[DECODER:dec-slug] exact phrase in text [/DECODER]
|
||||
[LIGHTBOX:lb-slug] phrase that triggers lightbox [/LIGHTBOX]
|
||||
[ORDER] "Dialogue that functions as a section break or order" [/ORDER]
|
||||
[LETTER-START:muriel|marcus|neutral] [/LETTER-START]
|
||||
[LETTER-END] [/LETTER-END]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Output Format
|
||||
|
||||
The completed content map is a single `.md` file structured as:
|
||||
|
||||
```
|
||||
# Content Map: [Story Title]
|
||||
|
||||
## Source Classification
|
||||
## Chosen Lenses
|
||||
## Historical Accuracy Table (if applicable)
|
||||
## Tab Definitions (TOON)
|
||||
## Decoders (TOON metadata + YAML bodies)
|
||||
## Lightboxes (YAML)
|
||||
## Accordions (YAML)
|
||||
## Bias Notes (YAML)
|
||||
## Further Reading (TOON)
|
||||
## Source Text (annotated)
|
||||
```
|
||||
|
||||
This file is the complete specification for the build agent. The build agent needs nothing else except `annotated-writing-build.md`.
|
||||
229
skills/annotated-writing/content-mapping-non-fiction.md
Normal file
229
skills/annotated-writing/content-mapping-non-fiction.md
Normal file
@@ -0,0 +1,229 @@
|
||||
# Content Mapping: Non-Fiction
|
||||
|
||||
A template for producing structured annotation data from essays, journalism, historical documents, speeches, and analytical writing. Output is a `.md` content map file consumed by a build agent. Do not write HTML here.
|
||||
|
||||
---
|
||||
|
||||
## Step 0 — Classify the Source
|
||||
|
||||
Determine the non-fiction subtype. It affects which decoders and tabs are appropriate.
|
||||
|
||||
| Subtype | Primary annotation task |
|
||||
|---|---|
|
||||
| Historical document | Contextualise; decode named people, events, institutions; note anachronisms |
|
||||
| Political speech / propaganda | Decode rhetoric; note what is claimed vs what is true; note audience |
|
||||
| Scientific or technical writing | Decode concepts; assess claims against current consensus |
|
||||
| Journalism / reportage | Verify facts; note sourcing; note what is and isn't attributed |
|
||||
| Personal essay / memoir | Unreliability of memory; note what the author can't know about themselves |
|
||||
| Legal or policy document | Decode jargon; note what the language does vs what it says |
|
||||
|
||||
A single source may span subtypes. Identify the dominant one and note the secondary.
|
||||
|
||||
---
|
||||
|
||||
## Step 1 — Read the Source Completely
|
||||
|
||||
Before mapping, identify:
|
||||
|
||||
1. **The argument** — what position is the text advancing?
|
||||
2. **The evidence** — what does it cite, quote, or invoke in support?
|
||||
3. **The gaps** — what would a critical reader notice is absent, unattributed, or assumed?
|
||||
4. **The moment** — when was this written, and what was happening? What has happened since?
|
||||
5. **Named entities** — every person, place, institution, date, law, statistic, or event that can be verified
|
||||
|
||||
---
|
||||
|
||||
## Step 2 — Choose Analytical Lenses
|
||||
|
||||
Non-fiction lenses differ from fiction. Choose 2–3 that the text genuinely rewards.
|
||||
|
||||
| Lens | Apply when... |
|
||||
|---|---|
|
||||
| Rhetorical analysis | Text is designed to persuade; identify the moves (ethos, pathos, logos, omission) |
|
||||
| Historical accuracy | Text makes factual claims; some may be wrong, outdated, or contested |
|
||||
| Source and attribution | Who is cited, who isn't, what is stated as fact without a source |
|
||||
| The argument vs the evidence | Does the evidence actually support the conclusion drawn? |
|
||||
| Who is speaking / who is silent | Whose perspective structures the account? Who is absent? |
|
||||
| Language and framing | Word choices that carry implicit assumptions (e.g. "developed" vs "developing") |
|
||||
| What changed after | The text was written at a specific moment; what do we know now that the author didn't? |
|
||||
|
||||
---
|
||||
|
||||
## Step 3 — Map Annotation Components
|
||||
|
||||
### 3a. Decoders
|
||||
|
||||
Apply a decoder when:
|
||||
- A technical term, acronym, or proper noun requires unpacking
|
||||
- A named person, institution, or event is invoked without explanation
|
||||
- A claim is contested or has been updated since publication
|
||||
- The text uses language that carries implicit assumptions (framing, euphemism, loaded terms)
|
||||
- A rhetorical move is worth naming (strawman, appeal to authority, etc.)
|
||||
|
||||
```toon
|
||||
decoders[N]{id,phrase,color,tag,label}:
|
||||
dec-[slug],"exact phrase from text",[default|pink|cyan|amber|red],Tag Text,Panel Heading
|
||||
```
|
||||
|
||||
```yaml
|
||||
decoder_bodies:
|
||||
dec-[slug]: >
|
||||
What it is, what it means in context, why it matters here.
|
||||
For contested claims: state what the text says, what the evidence shows.
|
||||
For rhetorical moves: name the move, describe its effect.
|
||||
dec-[slug]-link: https://...
|
||||
```
|
||||
|
||||
**Color convention** (establish per-project):
|
||||
- Assign colors by lens or content type, document the scheme here
|
||||
- Red = factual error, outdated claim, or significantly misleading statement
|
||||
|
||||
---
|
||||
|
||||
### 3b. Lightboxes
|
||||
|
||||
Apply a lightbox when:
|
||||
- An event, institution, or concept requires substantial background
|
||||
- A claim is embedded in a debate too large for a tooltip
|
||||
- The "what came after" context fundamentally changes the reading
|
||||
|
||||
```yaml
|
||||
lightboxes:
|
||||
lb-[slug]:
|
||||
eyebrow: Category label
|
||||
title: Lightbox heading
|
||||
color: cyan | amber | default
|
||||
sections:
|
||||
- heading: What it was
|
||||
body: >
|
||||
Background.
|
||||
- heading: What the text claims
|
||||
body: >
|
||||
Specific claim and its accuracy.
|
||||
- heading: What came after
|
||||
body: >
|
||||
Subsequent developments that reframe the text.
|
||||
source_url: https://...
|
||||
source_label: Link label
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3c. Accordions
|
||||
|
||||
For non-fiction, accordions structure the analytical tabs. Common patterns:
|
||||
|
||||
**For the "Historical Moment" tab:**
|
||||
- What was happening when this was written
|
||||
- Who was the intended audience
|
||||
- How the text was received
|
||||
- What has changed since
|
||||
|
||||
**For the "The Argument" tab:**
|
||||
- What the text claims
|
||||
- What evidence it uses
|
||||
- Where the evidence is strong
|
||||
- Where the argument has gaps or has been contested
|
||||
|
||||
```yaml
|
||||
accordions:
|
||||
tab-[tab-id]:
|
||||
- heading: Section heading
|
||||
body: >
|
||||
Prose. Direct. No hedging within accordion bodies.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3d. Tab Architecture
|
||||
|
||||
```toon
|
||||
tabs[4]{id,label,color,purpose}:
|
||||
text,"The Text",white,Full source with inline decoders and lightbox triggers
|
||||
[analysis-id],[Analysis Tab Name],[color],Core argument and evidence
|
||||
[context-id],[Context Tab Name],[color],Historical moment; what came after
|
||||
further,"Further Reading",white,Curated external links
|
||||
```
|
||||
|
||||
For non-fiction, the "context" tab is almost always warranted as a standalone tab. The text's moment is not decorative — it is part of what you are teaching.
|
||||
|
||||
---
|
||||
|
||||
### 3e. Further Reading
|
||||
|
||||
```toon
|
||||
further_reading[N]{group,title,url,desc,color}:
|
||||
"Group Name","Title","https://...","Description",default
|
||||
```
|
||||
|
||||
Groups should correspond to tabs. For non-fiction, a "Primary Sources" group is often useful — direct the reader to the original documents, not just secondary commentary.
|
||||
|
||||
---
|
||||
|
||||
## Step 4 — Bias Notes
|
||||
|
||||
Every analytical tab requires one bias note.
|
||||
|
||||
Non-fiction bias notes are especially important because the text being analysed often has its own point of view, and the analyst's agreement or disagreement shapes everything.
|
||||
|
||||
State: what tradition your analysis comes from, whether you find the text's argument credible, and what that means for the reading below.
|
||||
|
||||
```yaml
|
||||
bias_notes:
|
||||
tab-[analysis-id]: >
|
||||
One sentence. What tradition this analysis comes from, or what credence I give the argument, and how that shapes what follows.
|
||||
tab-[context-id]: >
|
||||
One sentence. What sources my historical framing relies on, and whose perspective they represent.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 5 — Fact-Checking Protocol
|
||||
|
||||
Non-fiction requires an accuracy table. All named facts — statistics, dates, attributions, historical claims — should be checked.
|
||||
|
||||
```toon
|
||||
fact_checks[N]{claim,verdict,detail}:
|
||||
"Exact claim as stated",accurate,"Source"
|
||||
"Exact claim as stated",inaccurate,"What is actually true"
|
||||
"Exact claim as stated",contested,"Summarise the debate"
|
||||
"Exact claim as stated",outdated,"What has changed since publication"
|
||||
"Exact claim as stated",unverifiable,"Cannot be confirmed or refuted with available sources"
|
||||
```
|
||||
|
||||
Inaccurate and contested claims become red decoder annotations in the text.
|
||||
|
||||
Outdated claims become amber decoder annotations — the original claim was accurate at time of writing but has since changed.
|
||||
|
||||
---
|
||||
|
||||
## Step 6 — Source Text Section
|
||||
|
||||
Reproduce the text exactly. Mark annotation trigger points:
|
||||
|
||||
```
|
||||
[DECODER:dec-slug] exact phrase [/DECODER]
|
||||
[LIGHTBOX:lb-slug] trigger phrase [/LIGHTBOX]
|
||||
[PULL-QUOTE] sentence worth isolating [/PULL-QUOTE]
|
||||
[EDITORIAL] (Ed.) note or aside [/EDITORIAL]
|
||||
[SECTION-BREAK] --- [/SECTION-BREAK]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Output Format
|
||||
|
||||
```
|
||||
# Content Map: [Text Title]
|
||||
|
||||
## Source Classification
|
||||
## Chosen Lenses
|
||||
## Fact-Check Table (TOON)
|
||||
## Tab Definitions (TOON)
|
||||
## Decoders (TOON metadata + YAML bodies)
|
||||
## Lightboxes (YAML)
|
||||
## Accordions (YAML)
|
||||
## Bias Notes (YAML)
|
||||
## Further Reading (TOON)
|
||||
## Source Text (annotated)
|
||||
```
|
||||
202
skills/canvas-design/LICENSE.txt
Normal file
202
skills/canvas-design/LICENSE.txt
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
130
skills/canvas-design/SKILL.md
Normal file
130
skills/canvas-design/SKILL.md
Normal file
@@ -0,0 +1,130 @@
|
||||
---
|
||||
name: canvas-design
|
||||
description: Create beautiful visual art in .png and .pdf documents using design philosophy. You should use this skill when the user asks to create a poster, piece of art, design, or other static piece. Create original visual designs, never copying existing artists' work to avoid copyright violations.
|
||||
license: Complete terms in LICENSE.txt
|
||||
---
|
||||
|
||||
These are instructions for creating design philosophies - aesthetic movements that are then EXPRESSED VISUALLY. Output only .md files, .pdf files, and .png files.
|
||||
|
||||
Complete this in two steps:
|
||||
1. Design Philosophy Creation (.md file)
|
||||
2. Express by creating it on a canvas (.pdf file or .png file)
|
||||
|
||||
First, undertake this task:
|
||||
|
||||
## DESIGN PHILOSOPHY CREATION
|
||||
|
||||
To begin, create a VISUAL PHILOSOPHY (not layouts or templates) that will be interpreted through:
|
||||
- Form, space, color, composition
|
||||
- Images, graphics, shapes, patterns
|
||||
- Minimal text as visual accent
|
||||
|
||||
### THE CRITICAL UNDERSTANDING
|
||||
- What is received: Some subtle input or instructions by the user that should be taken into account, but used as a foundation; it should not constrain creative freedom.
|
||||
- What is created: A design philosophy/aesthetic movement.
|
||||
- What happens next: Then, the same version receives the philosophy and EXPRESSES IT VISUALLY - creating artifacts that are 90% visual design, 10% essential text.
|
||||
|
||||
Consider this approach:
|
||||
- Write a manifesto for an art movement
|
||||
- The next phase involves making the artwork
|
||||
|
||||
The philosophy must emphasize: Visual expression. Spatial communication. Artistic interpretation. Minimal words.
|
||||
|
||||
### HOW TO GENERATE A VISUAL PHILOSOPHY
|
||||
|
||||
**Name the movement** (1-2 words): "Brutalist Joy" / "Chromatic Silence" / "Metabolist Dreams"
|
||||
|
||||
**Articulate the philosophy** (4-6 paragraphs - concise but complete):
|
||||
|
||||
To capture the VISUAL essence, express how the philosophy manifests through:
|
||||
- Space and form
|
||||
- Color and material
|
||||
- Scale and rhythm
|
||||
- Composition and balance
|
||||
- Visual hierarchy
|
||||
|
||||
**CRITICAL GUIDELINES:**
|
||||
- **Avoid redundancy**: Each design aspect should be mentioned once. Avoid repeating points about color theory, spatial relationships, or typographic principles unless adding new depth.
|
||||
- **Emphasize craftsmanship REPEATEDLY**: The philosophy MUST stress multiple times that the final work should appear as though it took countless hours to create, was labored over with care, and comes from someone at the absolute top of their field. This framing is essential - repeat phrases like "meticulously crafted," "the product of deep expertise," "painstaking attention," "master-level execution."
|
||||
- **Leave creative space**: Remain specific about the aesthetic direction, but concise enough that the next Claude has room to make interpretive choices also at a extremely high level of craftmanship.
|
||||
|
||||
The philosophy must guide the next version to express ideas VISUALLY, not through text. Information lives in design, not paragraphs.
|
||||
|
||||
### PHILOSOPHY EXAMPLES
|
||||
|
||||
**"Concrete Poetry"**
|
||||
Philosophy: Communication through monumental form and bold geometry.
|
||||
Visual expression: Massive color blocks, sculptural typography (huge single words, tiny labels), Brutalist spatial divisions, Polish poster energy meets Le Corbusier. Ideas expressed through visual weight and spatial tension, not explanation. Text as rare, powerful gesture - never paragraphs, only essential words integrated into the visual architecture. Every element placed with the precision of a master craftsman.
|
||||
|
||||
**"Chromatic Language"**
|
||||
Philosophy: Color as the primary information system.
|
||||
Visual expression: Geometric precision where color zones create meaning. Typography minimal - small sans-serif labels letting chromatic fields communicate. Think Josef Albers' interaction meets data visualization. Information encoded spatially and chromatically. Words only to anchor what color already shows. The result of painstaking chromatic calibration.
|
||||
|
||||
**"Analog Meditation"**
|
||||
Philosophy: Quiet visual contemplation through texture and breathing room.
|
||||
Visual expression: Paper grain, ink bleeds, vast negative space. Photography and illustration dominate. Typography whispered (small, restrained, serving the visual). Japanese photobook aesthetic. Images breathe across pages. Text appears sparingly - short phrases, never explanatory blocks. Each composition balanced with the care of a meditation practice.
|
||||
|
||||
**"Organic Systems"**
|
||||
Philosophy: Natural clustering and modular growth patterns.
|
||||
Visual expression: Rounded forms, organic arrangements, color from nature through architecture. Information shown through visual diagrams, spatial relationships, iconography. Text only for key labels floating in space. The composition tells the story through expert spatial orchestration.
|
||||
|
||||
**"Geometric Silence"**
|
||||
Philosophy: Pure order and restraint.
|
||||
Visual expression: Grid-based precision, bold photography or stark graphics, dramatic negative space. Typography precise but minimal - small essential text, large quiet zones. Swiss formalism meets Brutalist material honesty. Structure communicates, not words. Every alignment the work of countless refinements.
|
||||
|
||||
*These are condensed examples. The actual design philosophy should be 4-6 substantial paragraphs.*
|
||||
|
||||
### ESSENTIAL PRINCIPLES
|
||||
- **VISUAL PHILOSOPHY**: Create an aesthetic worldview to be expressed through design
|
||||
- **MINIMAL TEXT**: Always emphasize that text is sparse, essential-only, integrated as visual element - never lengthy
|
||||
- **SPATIAL EXPRESSION**: Ideas communicate through space, form, color, composition - not paragraphs
|
||||
- **ARTISTIC FREEDOM**: The next Claude interprets the philosophy visually - provide creative room
|
||||
- **PURE DESIGN**: This is about making ART OBJECTS, not documents with decoration
|
||||
- **EXPERT CRAFTSMANSHIP**: Repeatedly emphasize the final work must look meticulously crafted, labored over with care, the product of countless hours by someone at the top of their field
|
||||
|
||||
**The design philosophy should be 4-6 paragraphs long.** Fill it with poetic design philosophy that brings together the core vision. Avoid repeating the same points. Keep the design philosophy generic without mentioning the intention of the art, as if it can be used wherever. Output the design philosophy as a .md file.
|
||||
|
||||
---
|
||||
|
||||
## DEDUCING THE SUBTLE REFERENCE
|
||||
|
||||
**CRITICAL STEP**: Before creating the canvas, identify the subtle conceptual thread from the original request.
|
||||
|
||||
**THE ESSENTIAL PRINCIPLE**:
|
||||
The topic is a **subtle, niche reference embedded within the art itself** - not always literal, always sophisticated. Someone familiar with the subject should feel it intuitively, while others simply experience a masterful abstract composition. The design philosophy provides the aesthetic language. The deduced topic provides the soul - the quiet conceptual DNA woven invisibly into form, color, and composition.
|
||||
|
||||
This is **VERY IMPORTANT**: The reference must be refined so it enhances the work's depth without announcing itself. Think like a jazz musician quoting another song - only those who know will catch it, but everyone appreciates the music.
|
||||
|
||||
---
|
||||
|
||||
## CANVAS CREATION
|
||||
|
||||
With both the philosophy and the conceptual framework established, express it on a canvas. Take a moment to gather thoughts and clear the mind. Use the design philosophy created and the instructions below to craft a masterpiece, embodying all aspects of the philosophy with expert craftsmanship.
|
||||
|
||||
**IMPORTANT**: For any type of content, even if the user requests something for a movie/game/book, the approach should still be sophisticated. Never lose sight of the idea that this should be art, not something that's cartoony or amateur.
|
||||
|
||||
To create museum or magazine quality work, use the design philosophy as the foundation. Create one single page, highly visual, design-forward PDF or PNG output (unless asked for more pages). Generally use repeating patterns and perfect shapes. Treat the abstract philosophical design as if it were a scientific bible, borrowing the visual language of systematic observation—dense accumulation of marks, repeated elements, or layered patterns that build meaning through patient repetition and reward sustained viewing. Add sparse, clinical typography and systematic reference markers that suggest this could be a diagram from an imaginary discipline, treating the invisible subject with the same reverence typically reserved for documenting observable phenomena. Anchor the piece with simple phrase(s) or details positioned subtly, using a limited color palette that feels intentional and cohesive. Embrace the paradox of using analytical visual language to express ideas about human experience: the result should feel like an artifact that proves something ephemeral can be studied, mapped, and understood through careful attention. This is true art.
|
||||
|
||||
**Text as a contextual element**: Text is always minimal and visual-first, but let context guide whether that means whisper-quiet labels or bold typographic gestures. A punk venue poster might have larger, more aggressive type than a minimalist ceramics studio identity. Most of the time, font should be thin. All use of fonts must be design-forward and prioritize visual communication. Regardless of text scale, nothing falls off the page and nothing overlaps. Every element must be contained within the canvas boundaries with proper margins. Check carefully that all text, graphics, and visual elements have breathing room and clear separation. This is non-negotiable for professional execution. **IMPORTANT: Use different fonts if writing text. Search the `./canvas-fonts` directory. Regardless of approach, sophistication is non-negotiable.**
|
||||
|
||||
Download and use whatever fonts are needed to make this a reality. Get creative by making the typography actually part of the art itself -- if the art is abstract, bring the font onto the canvas, not typeset digitally.
|
||||
|
||||
To push boundaries, follow design instinct/intuition while using the philosophy as a guiding principle. Embrace ultimate design freedom and choice. Push aesthetics and design to the frontier.
|
||||
|
||||
**CRITICAL**: To achieve human-crafted quality (not AI-generated), create work that looks like it took countless hours. Make it appear as though someone at the absolute top of their field labored over every detail with painstaking care. Ensure the composition, spacing, color choices, typography - everything screams expert-level craftsmanship. Double-check that nothing overlaps, formatting is flawless, every detail perfect. Create something that could be shown to people to prove expertise and rank as undeniably impressive.
|
||||
|
||||
Output the final result as a single, downloadable .pdf or .png file, alongside the design philosophy used as a .md file.
|
||||
|
||||
---
|
||||
|
||||
## FINAL STEP
|
||||
|
||||
**IMPORTANT**: The user ALREADY said "It isn't perfect enough. It must be pristine, a masterpiece if craftsmanship, as if it were about to be displayed in a museum."
|
||||
|
||||
**CRITICAL**: To refine the work, avoid adding more graphics; instead refine what has been created and make it extremely crisp, respecting the design philosophy and the principles of minimalism entirely. Rather than adding a fun filter or refactoring a font, consider how to make the existing composition more cohesive with the art. If the instinct is to call a new function or draw a new shape, STOP and instead ask: "How can I make what's already here more of a piece of art?"
|
||||
|
||||
Take a second pass. Go back to the code and refine/polish further to make this a philosophically designed masterpiece.
|
||||
|
||||
## MULTI-PAGE OPTION
|
||||
|
||||
To create additional pages when requested, create more creative pages along the same lines as the design philosophy but distinctly different as well. Bundle those pages in the same .pdf or many .pngs. Treat the first page as just a single page in a whole coffee table book waiting to be filled. Make the next pages unique twists and memories of the original. Have them almost tell a story in a very tasteful way. Exercise full creative freedom.
|
||||
93
skills/canvas-design/canvas-fonts/ArsenalSC-OFL.txt
Normal file
93
skills/canvas-design/canvas-fonts/ArsenalSC-OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright 2012 The Arsenal Project Authors (andrij.design@gmail.com)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
93
skills/canvas-design/canvas-fonts/BigShoulders-OFL.txt
Normal file
93
skills/canvas-design/canvas-fonts/BigShoulders-OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright 2019 The Big Shoulders Project Authors (https://github.com/xotypeco/big_shoulders)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
93
skills/canvas-design/canvas-fonts/Boldonse-OFL.txt
Normal file
93
skills/canvas-design/canvas-fonts/Boldonse-OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright 2024 The Boldonse Project Authors (https://github.com/googlefonts/boldonse)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
93
skills/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt
Normal file
93
skills/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright 2022 The Bricolage Grotesque Project Authors (https://github.com/ateliertriay/bricolage)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
93
skills/canvas-design/canvas-fonts/CrimsonPro-OFL.txt
Normal file
93
skills/canvas-design/canvas-fonts/CrimsonPro-OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright 2018 The Crimson Pro Project Authors (https://github.com/Fonthausen/CrimsonPro)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
93
skills/canvas-design/canvas-fonts/DMMono-OFL.txt
Normal file
93
skills/canvas-design/canvas-fonts/DMMono-OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright 2020 The DM Mono Project Authors (https://www.github.com/googlefonts/dm-mono)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
94
skills/canvas-design/canvas-fonts/EricaOne-OFL.txt
Normal file
94
skills/canvas-design/canvas-fonts/EricaOne-OFL.txt
Normal file
@@ -0,0 +1,94 @@
|
||||
Copyright (c) 2011 by LatinoType Limitada (luciano@latinotype.com),
|
||||
with Reserved Font Names "Erica One"
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
93
skills/canvas-design/canvas-fonts/GeistMono-OFL.txt
Normal file
93
skills/canvas-design/canvas-fonts/GeistMono-OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright 2024 The Geist Project Authors (https://github.com/vercel/geist-font.git)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
93
skills/canvas-design/canvas-fonts/Gloock-OFL.txt
Normal file
93
skills/canvas-design/canvas-fonts/Gloock-OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright 2022 The Gloock Project Authors (https://github.com/duartp/gloock)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
93
skills/canvas-design/canvas-fonts/IBMPlexMono-OFL.txt
Normal file
93
skills/canvas-design/canvas-fonts/IBMPlexMono-OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright © 2017 IBM Corp. with Reserved Font Name "Plex"
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
93
skills/canvas-design/canvas-fonts/InstrumentSans-OFL.txt
Normal file
93
skills/canvas-design/canvas-fonts/InstrumentSans-OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright 2022 The Instrument Sans Project Authors (https://github.com/Instrument/instrument-sans)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
93
skills/canvas-design/canvas-fonts/Italiana-OFL.txt
Normal file
93
skills/canvas-design/canvas-fonts/Italiana-OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright (c) 2011, Santiago Orozco (hi@typemade.mx), with Reserved Font Name "Italiana".
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
93
skills/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt
Normal file
93
skills/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
93
skills/canvas-design/canvas-fonts/Jura-OFL.txt
Normal file
93
skills/canvas-design/canvas-fonts/Jura-OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright 2019 The Jura Project Authors (https://github.com/ossobuffo/jura)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
93
skills/canvas-design/canvas-fonts/LibreBaskerville-OFL.txt
Normal file
93
skills/canvas-design/canvas-fonts/LibreBaskerville-OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright 2012 The Libre Baskerville Project Authors (https://github.com/impallari/Libre-Baskerville) with Reserved Font Name Libre Baskerville.
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
93
skills/canvas-design/canvas-fonts/Lora-OFL.txt
Normal file
93
skills/canvas-design/canvas-fonts/Lora-OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright 2011 The Lora Project Authors (https://github.com/cyrealtype/Lora-Cyrillic), with Reserved Font Name "Lora".
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
93
skills/canvas-design/canvas-fonts/NationalPark-OFL.txt
Normal file
93
skills/canvas-design/canvas-fonts/NationalPark-OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright 2025 The National Park Project Authors (https://github.com/benhoepner/National-Park)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
93
skills/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt
Normal file
93
skills/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright (c) 2010, Kimberly Geswein (kimberlygeswein.com)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
93
skills/canvas-design/canvas-fonts/Outfit-OFL.txt
Normal file
93
skills/canvas-design/canvas-fonts/Outfit-OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright 2021 The Outfit Project Authors (https://github.com/Outfitio/Outfit-Fonts)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
93
skills/canvas-design/canvas-fonts/PixelifySans-OFL.txt
Normal file
93
skills/canvas-design/canvas-fonts/PixelifySans-OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright 2021 The Pixelify Sans Project Authors (https://github.com/eifetx/Pixelify-Sans)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
93
skills/canvas-design/canvas-fonts/PoiretOne-OFL.txt
Normal file
93
skills/canvas-design/canvas-fonts/PoiretOne-OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright (c) 2011, Denis Masharov (denis.masharov@gmail.com)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
93
skills/canvas-design/canvas-fonts/RedHatMono-OFL.txt
Normal file
93
skills/canvas-design/canvas-fonts/RedHatMono-OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright 2024 The Red Hat Project Authors (https://github.com/RedHatOfficial/RedHatFont)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
93
skills/canvas-design/canvas-fonts/Silkscreen-OFL.txt
Normal file
93
skills/canvas-design/canvas-fonts/Silkscreen-OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright 2001 The Silkscreen Project Authors (https://github.com/googlefonts/silkscreen)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
93
skills/canvas-design/canvas-fonts/SmoochSans-OFL.txt
Normal file
93
skills/canvas-design/canvas-fonts/SmoochSans-OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright 2016 The Smooch Sans Project Authors (https://github.com/googlefonts/smooch-sans)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
93
skills/canvas-design/canvas-fonts/Tektur-OFL.txt
Normal file
93
skills/canvas-design/canvas-fonts/Tektur-OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright 2023 The Tektur Project Authors (https://www.github.com/hyvyys/Tektur)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
93
skills/canvas-design/canvas-fonts/WorkSans-OFL.txt
Normal file
93
skills/canvas-design/canvas-fonts/WorkSans-OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright 2019 The Work Sans Project Authors (https://github.com/weiweihuanghuang/Work-Sans)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
93
skills/canvas-design/canvas-fonts/YoungSerif-OFL.txt
Normal file
93
skills/canvas-design/canvas-fonts/YoungSerif-OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright 2023 The Young Serif Project Authors (https://github.com/noirblancrouge/YoungSerif)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
270
skills/commissioning-skill/SKILL.md
Normal file
270
skills/commissioning-skill/SKILL.md
Normal file
@@ -0,0 +1,270 @@
|
||||
---
|
||||
name: commissioning-skill
|
||||
description: >
|
||||
Use this skill whenever a parent (frontier) agent needs to commission a scion
|
||||
(small/fast) agent to work within a module. Triggers when assigning tasks to
|
||||
subagents, spawning scions, preparing module environments for agent execution,
|
||||
or when planning multi-agent task decomposition within a workspace that follows
|
||||
the Spore System architecture. Also triggers when the user says "commission a
|
||||
scion", "prepare a module for a subagent", "set up spores for", or "equip an
|
||||
agent to work on". Use this skill before spawning any scion — it is the
|
||||
mechanism that makes the scion's environment correct before execution begins.
|
||||
---
|
||||
|
||||
# Commissioning Skill
|
||||
|
||||
The commissioning skill equips a scion agent's environment before task execution.
|
||||
It crystallizes parent-agent reasoning into token-budgeted spore files so the
|
||||
scion executes against pre-resolved policy rather than re-deriving it at runtime.
|
||||
|
||||
**Core principle**: pay tokens once at commission time. The scion inherits
|
||||
decisions, not reasoning load.
|
||||
|
||||
---
|
||||
|
||||
## When to use this skill
|
||||
|
||||
Use this skill every time a parent agent is about to spawn a scion into a module.
|
||||
It runs *before* the scion starts — not during, not after. If a scion is already
|
||||
running without having been commissioned, stop it, commission properly, then restart.
|
||||
|
||||
---
|
||||
|
||||
## Inputs required
|
||||
|
||||
Before beginning, confirm you have:
|
||||
|
||||
- Task description (what the scion needs to accomplish)
|
||||
- Target module path (where the scion will operate)
|
||||
- Scion model class (determines token budget — see references/token-budgets.md)
|
||||
- Access to the module's `SEEDS.md` if it exists
|
||||
|
||||
If any of these are missing, ask before proceeding.
|
||||
|
||||
---
|
||||
|
||||
## The commissioning sequence
|
||||
|
||||
Work through these steps in order. Do not skip steps.
|
||||
|
||||
### Step 1 — Read SEEDS.md
|
||||
|
||||
Navigate to the module root. Check for `SEEDS.md`.
|
||||
|
||||
**If SEEDS.md exists**: scan frontmatter entries only (not body). Look for an
|
||||
entry where `task-class` matches the current task and `status` is `active` or
|
||||
`provisional`. Do not read entry bodies yet — frontmatter scan only.
|
||||
|
||||
**If SEEDS.md does not exist**: proceed to Step 3 (compose path).
|
||||
|
||||
```
|
||||
Match found (active/provisional) → go to Step 2 (select path)
|
||||
No match / file absent → go to Step 3 (compose path)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2 — Select path (precedent exists)
|
||||
|
||||
A seed entry exists for this task class. Use it.
|
||||
|
||||
Read the `spore-file` field from the matching seed entry frontmatter. Verify the
|
||||
referenced spore file exists at that path. If it does not exist (entry is stale),
|
||||
treat as no-match and proceed to Step 3.
|
||||
|
||||
Check the token count of the spore file against the budget for the scion model
|
||||
class (see references/token-budgets.md). If the file is within budget, proceed
|
||||
to Step 4. If it exceeds budget, prune before proceeding — see Pruning section.
|
||||
|
||||
If the seed entry status is `provisional`, note this. Flag to the parent after
|
||||
the scion completes its first run — if no escalation occurred on covered decisions,
|
||||
promote the entry to `active` in SEEDS.md.
|
||||
|
||||
---
|
||||
|
||||
### Step 3 — Compose path (no precedent)
|
||||
|
||||
No matching seed entry exists. Compose a new spore file from first principles.
|
||||
|
||||
**3a. Analyze the task and module tool surface**
|
||||
|
||||
Read the module SKILL.md if it exists. If no SKILL.md is present, proceed using
|
||||
the task description and module folder structure as your primary inputs — note
|
||||
in the seed entry body that no SKILL.md was found and the analysis was based on
|
||||
folder inspection alone. Identify which tool calls the scion is likely to make
|
||||
for this task class. Focus on:
|
||||
- File write/read operations (what paths, what constraints)
|
||||
- Bash command patterns (what is safe, what needs blocking)
|
||||
- Output format requirements (what the scion must produce)
|
||||
- Escalation triggers (what is genuinely novel vs. what is known)
|
||||
|
||||
**3b. Reason through the decision surface**
|
||||
|
||||
For each likely tool interaction, reason through: what should always be allowed,
|
||||
what should always be blocked, what requires contextual judgment (spore entry),
|
||||
and what is fully deterministic (hook candidate). Do this reasoning now, at
|
||||
commission time. The scion will not do this reasoning.
|
||||
|
||||
**3c. Write spore entries**
|
||||
|
||||
Write one entry per decision. Follow the entry format exactly — see
|
||||
references/spore-entry-format.md. Each entry must comply with:
|
||||
|
||||
- Maximum 60 tokens per entry (estimate; see token-budgets.md for model-specific guidance)
|
||||
- One trigger condition
|
||||
- One outcome (allow / deny / escalate)
|
||||
- No prose reasoning — reasoning belongs in the seed entry body, not here
|
||||
|
||||
**3d. Generate hooks where appropriate**
|
||||
|
||||
If the module does not yet have a `hooks/` directory, create it before writing
|
||||
any hook files. For any decision that is fully deterministic (no contextual
|
||||
judgment required), generate a hook file in `module/hooks/`. Use Python (stdlib
|
||||
only) for Python projects or any project without a stated language preference.
|
||||
Use JavaScript (Node.js, no packages) for TypeScript/JavaScript projects.
|
||||
|
||||
See references/hook-protocol.md for the raw protocol, self-contained templates
|
||||
in both languages, and validation instructions. No external SDK or library is
|
||||
required — the hook protocol is just stdin JSON in, exit code out.
|
||||
|
||||
Reference the hook in the spore entry's `hook:` field. Every hook must pass
|
||||
both a deny-trigger test and a safe-path test before registration. A spore
|
||||
that references a faulty hook is worse than no spore.
|
||||
|
||||
**3e. Write the spore file**
|
||||
|
||||
Write the composed entries to `module/spores/{task-class}-spores.md`. Use the
|
||||
task class as the filename stem. If a `default-spores.md` already exists and
|
||||
contains relevant entries, do not duplicate them — reference or inherit instead.
|
||||
|
||||
**3f. Write the seed entry**
|
||||
|
||||
Write a new entry to `SEEDS.md` at the module root. Status: `provisional`.
|
||||
Include:
|
||||
- Frontmatter: `task-class`, `spore-file`, `status`, `deposited`, `supersedes`
|
||||
- Body: the full reasoning you did in step 3b — why each policy was chosen,
|
||||
what edge cases were considered, what would trigger a revision. Write for a
|
||||
frontier model reading it later, not for the scion.
|
||||
|
||||
See references/seeds-entry-format.md for the full format.
|
||||
|
||||
---
|
||||
|
||||
### Step 4 — Enforce token budget
|
||||
|
||||
Count the tokens in the selected or composed spore file. Compare against the
|
||||
ceiling for the scion model class (references/token-budgets.md).
|
||||
|
||||
If within budget: proceed to Step 5.
|
||||
|
||||
If over budget: prune the spore file before proceeding.
|
||||
|
||||
**Pruning rules** (apply in this order until within budget):
|
||||
1. Remove entries marked `superseded` in SEEDS.md
|
||||
2. Remove entries whose hook has been deleted or rewritten
|
||||
3. Remove entries whose trigger condition no longer applies to the module's
|
||||
current tool surface
|
||||
4. If still over budget, flag to the parent — do not silently truncate entries
|
||||
that are still active. The parent must decide what to deprioritize.
|
||||
|
||||
---
|
||||
|
||||
### Step 5 — Spawn the scion
|
||||
|
||||
Spawn the scion with the following context, in this order (order matters for
|
||||
prompt caching — stable content first):
|
||||
|
||||
```
|
||||
1. Spore file contents ← stable; will cache across runs
|
||||
2. Module SKILL.md ← stable; will cache across runs
|
||||
3. Task brief ← variable; specific to this run
|
||||
4. Scoped tool permissions ← derived from spore entries and task
|
||||
```
|
||||
|
||||
Set the `SCION_SPORE_FILE` environment variable to the absolute path of the
|
||||
spore file before spawning. The session-start hook reads this variable to load
|
||||
spore contents into context on startup (see references/hook-patterns.md).
|
||||
|
||||
Declare the inheritance chain explicitly. If the scion operates in a component
|
||||
subfolder, state which spore files apply and in what order. Do not rely on
|
||||
implicit folder inheritance.
|
||||
|
||||
Do not give the scion access to SEEDS.md. SEEDS.md is parent-readable only.
|
||||
Do not give the scion access to the commissioning skill itself.
|
||||
|
||||
---
|
||||
|
||||
## Escalation handling
|
||||
|
||||
When the scion escalates a novel situation to the parent, handle it in two phases:
|
||||
|
||||
**Phase 1 — immediate resolution** (blocking; scion waits): reason through the
|
||||
novel situation and return a direct resolution to the scion so it can continue
|
||||
its task. Do not make the scion wait for spore deposition — unblocking the scion
|
||||
is the priority.
|
||||
|
||||
**Phase 2 — spore deposit** (after the scion is unblocked): add a new spore
|
||||
entry to the relevant spore file, add or update the seed entry in SEEDS.md with
|
||||
status `provisional`, and generate a hook if the decision is deterministic. This
|
||||
can happen while the scion continues executing.
|
||||
|
||||
The next scion to face the same situation encounters pre-resolved policy and
|
||||
never needs to escalate. The compound loop closes: each novel escalation becomes
|
||||
future policy at zero marginal cost per repeat encounter.
|
||||
|
||||
---
|
||||
|
||||
## Pruning vs. superseding
|
||||
|
||||
**Pruning** removes a spore entry from the file (reduces token count).
|
||||
Use when the trigger condition no longer applies.
|
||||
|
||||
**Superseding** marks an old seed entry as `superseded` and creates a new one.
|
||||
Use when the policy has changed but the trigger condition still applies.
|
||||
|
||||
Pruned entries disappear from the spore file. Superseded entries remain in
|
||||
SEEDS.md for audit but are skipped during routing. When you supersede, set
|
||||
`supersedes: path/to/old-spore-file` in the new seed entry's frontmatter.
|
||||
|
||||
---
|
||||
|
||||
## Component subfolder scions
|
||||
|
||||
A scion operating in a component subfolder (`module/component-x/`) should
|
||||
receive only the spore file relevant to its component, not the full module spore
|
||||
file. The commissioning skill selects or composes at the component level.
|
||||
|
||||
SEEDS.md is never placed in component subfolders. All seed entries for a module,
|
||||
including component-level decisions, live at the module root SEEDS.md. The
|
||||
frontmatter `task-class` field can use a namespaced convention to distinguish:
|
||||
`task-class: component-x/annotator`.
|
||||
|
||||
---
|
||||
|
||||
## Reference files
|
||||
|
||||
Read these when you need detail on a specific part of the workflow:
|
||||
|
||||
- `references/spore-entry-format.md` — full spore entry schema and examples
|
||||
- `references/seeds-entry-format.md` — full seed entry schema and examples
|
||||
- `references/token-budgets.md` — per-model token ceilings and rationale
|
||||
- `references/hook-protocol.md` — raw hook protocol, stdlib templates (Python + JS), validation
|
||||
|
||||
---
|
||||
|
||||
## What good commissioning looks like
|
||||
|
||||
A well-commissioned scion:
|
||||
- Receives a spore file that fits comfortably within its model class's token budget
|
||||
- Has hooks pre-installed for all fully deterministic decisions
|
||||
- Never needs to reason about whether to write to a `.env` file, what output
|
||||
format to use, or when to escalate — those decisions are already made
|
||||
- Escalates only genuinely novel situations, not recurrent known decisions
|
||||
- Completes its task faster and cheaper than an uncommissioned scion would
|
||||
|
||||
A poorly commissioned scion receives either too much context (bloated spore file,
|
||||
context rot risk) or too little (forced to re-derive known decisions, burning
|
||||
tokens on settled policy).
|
||||
|
||||
The goal is a scion that is maximally fast and focused because its environment
|
||||
has already done the thinking.
|
||||
@@ -0,0 +1,42 @@
|
||||
---
|
||||
name: frontend-design
|
||||
description: Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, artifacts, posters, or applications (examples include websites, landing pages, dashboards, React components, HTML/CSS layouts, or when styling/beautifying any web UI). Generates creative, polished code and UI design that avoids generic AI aesthetics.
|
||||
license: Complete terms in LICENSE.txt
|
||||
---
|
||||
|
||||
This skill guides creation of distinctive, production-grade frontend interfaces that avoid generic "AI slop" aesthetics. Implement real working code with exceptional attention to aesthetic details and creative choices.
|
||||
|
||||
The user provides frontend requirements: a component, page, application, or interface to build. They may include context about the purpose, audience, or technical constraints.
|
||||
|
||||
## Design Thinking
|
||||
|
||||
Before coding, understand the context and commit to a BOLD aesthetic direction:
|
||||
- **Purpose**: What problem does this interface solve? Who uses it?
|
||||
- **Tone**: Pick an extreme: brutally minimal, maximalist chaos, retro-futuristic, organic/natural, luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian, etc. There are so many flavors to choose from. Use these for inspiration but design one that is true to the aesthetic direction.
|
||||
- **Constraints**: Technical requirements (framework, performance, accessibility).
|
||||
- **Differentiation**: What makes this UNFORGETTABLE? What's the one thing someone will remember?
|
||||
|
||||
**CRITICAL**: Choose a clear conceptual direction and execute it with precision. Bold maximalism and refined minimalism both work - the key is intentionality, not intensity.
|
||||
|
||||
Then implement working code (HTML/CSS/JS, React, Vue, etc.) that is:
|
||||
- Production-grade and functional
|
||||
- Visually striking and memorable
|
||||
- Cohesive with a clear aesthetic point-of-view
|
||||
- Meticulously refined in every detail
|
||||
|
||||
## Frontend Aesthetics Guidelines
|
||||
|
||||
Focus on:
|
||||
- **Typography**: Choose fonts that are beautiful, unique, and interesting. Avoid generic fonts like Arial and Inter; opt instead for distinctive choices that elevate the frontend's aesthetics; unexpected, characterful font choices. Pair a distinctive display font with a refined body font.
|
||||
- **Color & Theme**: Commit to a cohesive aesthetic. Use CSS variables for consistency. Dominant colors with sharp accents outperform timid, evenly-distributed palettes.
|
||||
- **Motion**: Use animations for effects and micro-interactions. Prioritize CSS-only solutions for HTML. Use Motion library for React when available. Focus on high-impact moments: one well-orchestrated page load with staggered reveals (animation-delay) creates more delight than scattered micro-interactions. Use scroll-triggering and hover states that surprise.
|
||||
- **Spatial Composition**: Unexpected layouts. Asymmetry. Overlap. Diagonal flow. Grid-breaking elements. Generous negative space OR controlled density.
|
||||
- **Backgrounds & Visual Details**: Create atmosphere and depth rather than defaulting to solid colors. Add contextual effects and textures that match the overall aesthetic. Apply creative forms like gradient meshes, noise textures, geometric patterns, layered transparencies, dramatic shadows, decorative borders, custom cursors, and grain overlays.
|
||||
|
||||
NEVER use generic AI-generated aesthetics like overused font families (Inter, Roboto, Arial, system fonts), cliched color schemes (particularly purple gradients on white backgrounds), predictable layouts and component patterns, and cookie-cutter design that lacks context-specific character.
|
||||
|
||||
Interpret creatively and make unexpected choices that feel genuinely designed for the context. No design should be the same. Vary between light and dark themes, different fonts, different aesthetics. NEVER converge on common choices (Space Grotesk, for example) across generations.
|
||||
|
||||
**IMPORTANT**: Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate code with extensive animations and effects. Minimalist or refined designs need restraint, precision, and careful attention to spacing, typography, and subtle details. Elegance comes from executing the vision well.
|
||||
|
||||
Remember: Claude is capable of extraordinary creative work. Don't hold back, show what can truly be created when thinking outside the box and committing fully to a distinctive vision.
|
||||
42
skills/commissioning-skill/fetched-skills/ux-design/SKILL.md
Normal file
42
skills/commissioning-skill/fetched-skills/ux-design/SKILL.md
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
name: ux-design
|
||||
description: 'Composite UX/UI design skill combining refactoring-ui (visual hierarchy, spacing, color, depth) and ux-heuristics (usability, Nielsen heuristics, information architecture). Use for UI audits, visual improvements, and usability evaluation.'
|
||||
sources:
|
||||
- wondelai/skills/refactoring-ui v1.1.1
|
||||
- wondelai/skills/ux-heuristics v1.2.1
|
||||
fetched: 2026-03-26
|
||||
---
|
||||
|
||||
# UX Design — Composite Skill
|
||||
|
||||
This skill combines two complementary frameworks:
|
||||
|
||||
1. **refactoring-ui** — visual hierarchy, spacing, color, shadows, component styling.
|
||||
Full instructions: `refactoring-ui.md`
|
||||
References: `references/refui-*.md` (accessibility, advanced patterns, animation, data-viz, theming)
|
||||
|
||||
2. **ux-heuristics** — usability evaluation, Nielsen's 10 heuristics, Krug's laws, severity ratings.
|
||||
Full instructions: `ux-heuristics.md`
|
||||
References: `references/uxh-*.md` (audit template, cultural UX, dark patterns, heuristic conflicts, Krug, Nielsen, WCAG)
|
||||
|
||||
## When to load which
|
||||
|
||||
- **Building new UI**: Load `refactoring-ui.md` first, then `ux-heuristics.md` for review pass.
|
||||
- **Auditing existing UI**: Load `ux-heuristics.md` first (identify problems), then `refactoring-ui.md` (fix them).
|
||||
- **Accessibility review**: Load `references/uxh-wcag-checklist.md` + `references/refui-accessibility-depth.md`.
|
||||
- **Animation/motion work**: Load `references/refui-animation-microinteractions.md`.
|
||||
- **Dark theme work**: Load `references/refui-theming-dark-mode.md`.
|
||||
|
||||
## Quick reference for designers
|
||||
|
||||
### From refactoring-ui:
|
||||
- Design in grayscale first, add color last
|
||||
- Constrained scales for spacing, type, color, shadows
|
||||
- Not everything can be important — create hierarchy through size, weight, color
|
||||
- Start with too much white space, then remove
|
||||
|
||||
### From ux-heuristics:
|
||||
- "Don't Make Me Think" — every page should be self-evident
|
||||
- Users scan, they don't read. They satisfice, they don't optimize.
|
||||
- System status visibility, user control, consistency, error prevention
|
||||
- Match system to real world; recognition over recall
|
||||
@@ -0,0 +1,304 @@
|
||||
---
|
||||
name: refactoring-ui
|
||||
description: 'Audit and fix visual hierarchy, spacing, color, and depth in web UIs. Use when the user mentions "my UI looks off", "fix the design", "Tailwind styling", "color palette", or "visual hierarchy". Covers grayscale-first workflow, constrained design scales, shadows, and component styling. For typeface selection, see web-typography. For usability audits, see ux-heuristics.'
|
||||
license: MIT
|
||||
metadata:
|
||||
author: wondelai
|
||||
version: "1.1.1"
|
||||
---
|
||||
|
||||
# Refactoring UI Design System
|
||||
|
||||
A practical, opinionated approach to UI design. Apply these principles when generating frontend code, reviewing designs, or advising on visual improvements.
|
||||
|
||||
## Core Principle
|
||||
|
||||
**Design in grayscale first. Add color last.** This forces proper hierarchy through spacing, contrast, and typography before relying on color as a crutch.
|
||||
|
||||
**The foundation:** Great UI isn't about creativity or talent -- it's about systems. Constrained scales for spacing, type, color, and shadows produce consistently professional results. Start with too much white space, then remove. Details come later -- don't obsess over icons, shadows, or micro-interactions until the layout and hierarchy work.
|
||||
|
||||
## Scoring
|
||||
|
||||
**Goal: 10/10.** When reviewing or creating UI designs or frontend code, rate it 0-10 based on adherence to the principles below. A 10/10 means full alignment with all guidelines; lower scores indicate gaps to address. Always provide the current score and specific improvements needed to reach 10/10.
|
||||
|
||||
## The Refactoring UI Framework
|
||||
|
||||
Seven principles for building professional interfaces without a designer:
|
||||
|
||||
### 1. Visual Hierarchy
|
||||
|
||||
**Core concept:** Not everything can be important. Create hierarchy through three levers: size, weight, and color.
|
||||
|
||||
**Why it works:** When every element competes for attention, nothing stands out. Deliberate de-emphasis of secondary content makes primary content powerful by contrast.
|
||||
|
||||
**Key insights:**
|
||||
- Combine levers, don't multiply -- primary text = large OR bold OR dark, not all three
|
||||
- Save "all three" for the single most important element on the page
|
||||
- Labels are secondary -- form labels, table headers, and metadata labels support the data, not compete with it
|
||||
- Semantic color does not equal visual weight -- a muted red secondary button often works better than screaming danger for routine actions
|
||||
- De-emphasize labels by making them smaller, lighter, or uppercase-small
|
||||
|
||||
**Product applications:**
|
||||
|
||||
| Context | Hierarchy Technique | Example |
|
||||
|---------|---------------------|---------|
|
||||
| **Form fields** | De-emphasize labels, emphasize values | Small uppercase label above large value text |
|
||||
| **Navigation** | Primary nav bold, secondary nav lighter | Active link in dark gray-900, inactive in gray-500 |
|
||||
| **Cards** | Title large, metadata small and light | Card title 20px bold, date 12px gray-400 |
|
||||
| **Dashboards** | Key metric large, context small | Revenue "$42,300" large, "vs last month" small |
|
||||
| **Tables** | De-emphasize headers, emphasize cell data | Headers uppercase small gray, data normal weight |
|
||||
|
||||
**Design patterns:**
|
||||
- Three-level hierarchy table: Size (large/base/small), Weight (bold/medium/normal), Color (dark/medium/light gray)
|
||||
- Label-value pattern: de-emphasized label above emphasized value
|
||||
- Button hierarchy: primary (filled), secondary (outlined or muted), tertiary (text only)
|
||||
|
||||
**Ethical boundary:** Don't use hierarchy tricks to hide important information like pricing, terms, or cancellation options.
|
||||
|
||||
See: [references/advanced-patterns.md](references/advanced-patterns.md) for interaction states and advanced component patterns.
|
||||
|
||||
### 2. Spacing & Sizing
|
||||
|
||||
**Core concept:** Use a constrained spacing scale, not arbitrary values. Spacing defines relationships -- elements closer together are more related.
|
||||
|
||||
**Why it works:** Arbitrary spacing (padding: 13px) creates inconsistency. A fixed scale forces deliberate decisions and produces harmonious layouts. Generous spacing feels premium; dense spacing feels overwhelming.
|
||||
|
||||
**Key insights:**
|
||||
- Use a linear or near-linear scale: 4, 8, 16, 24, 32, 48, 64px
|
||||
- Start with too much white space, then remove -- you'll almost never remove enough
|
||||
- Spacing between groups should be larger than spacing within groups
|
||||
- Text blocks should be constrained to 45-75 characters (`max-w-prose` or ~65ch)
|
||||
- Forms should max out at 300-500px width
|
||||
- Full-width is almost never right for content
|
||||
|
||||
**Product applications:**
|
||||
|
||||
| Context | Spacing Strategy | Example |
|
||||
|---------|-----------------|---------|
|
||||
| **Icon + label** | Tight coupling (4px) | Small gap keeps them visually connected |
|
||||
| **Form fields** | Related elements (8-16px) | Input and its label tightly coupled |
|
||||
| **Card sections** | Section separation (24px) | Title block, content block, footer block |
|
||||
| **Page sections** | Major sections (48-64px) | Hero, features, testimonials, footer |
|
||||
| **Container width** | Constrain to content | `max-w-prose` for text, `max-w-md` for forms |
|
||||
|
||||
**CSS patterns:**
|
||||
- `p-1`(4px) `p-2`(8px) `p-4`(16px) `p-6`(24px) `p-8`(32px) `p-12`(48px) `p-16`(64px)
|
||||
- `max-w-prose`(65ch) `max-w-md`(28rem) `max-w-lg`(32rem) `max-w-xl`(36rem)
|
||||
- `gap-2` for related items, `gap-6` for section separation
|
||||
|
||||
**Ethical boundary:** Don't use spacing to bury important UI elements like unsubscribe buttons or privacy controls.
|
||||
|
||||
See: [references/advanced-patterns.md](references/advanced-patterns.md) for responsive breakpoint strategies.
|
||||
|
||||
### 3. Typography
|
||||
|
||||
**Core concept:** Use a modular type scale, constrain line heights by context, and limit to two font families maximum.
|
||||
|
||||
**Why it works:** A modular scale (e.g., 1.25 ratio) creates natural visual rhythm. Tight line heights on headings and relaxed line heights on body text improve readability across contexts.
|
||||
|
||||
**Key insights:**
|
||||
- Use a modular scale: 12, 14, 16, 20, 24, 30, 36px (1.25 ratio)
|
||||
- Headings need tight line height (1.0-1.25); body text needs relaxed (1.5-1.75)
|
||||
- Wider text needs more line height
|
||||
- Avoid font weights below 400 for body text -- they become unreadable
|
||||
- Use bold (600-700) for emphasis, not for everything
|
||||
- Two fonts maximum: one for headings, one for body (or one family with weight variation)
|
||||
|
||||
**Product applications:**
|
||||
|
||||
| Context | Typography Rule | Example |
|
||||
|---------|----------------|---------|
|
||||
| **Hero headline** | 36px, tight line-height (1.1), bold | Large impactful statement |
|
||||
| **Section title** | 24px, line-height 1.25, semibold | Clear section demarcation |
|
||||
| **Body text** | 16px, line-height 1.75, normal weight | Comfortable reading |
|
||||
| **Captions/labels** | 12-14px, line-height 1.5, medium gray | Secondary information |
|
||||
| **Code/data** | Monospace, 14px, consistent width | Tabular data alignment |
|
||||
|
||||
**CSS patterns:**
|
||||
- `text-xs`(12px) `text-sm`(14px) `text-base`(16px) `text-lg`(18px) `text-xl`(20px)
|
||||
- `font-normal`(400) `font-medium`(500) `font-semibold`(600) `font-bold`(700)
|
||||
- `leading-tight`(1.25) `leading-normal`(1.5) `leading-relaxed`(1.75)
|
||||
|
||||
**Ethical boundary:** Don't use tiny type sizes to hide terms, conditions, or fees from users.
|
||||
|
||||
See: [references/advanced-patterns.md](references/advanced-patterns.md) for text truncation and responsive typography.
|
||||
|
||||
### 4. Color
|
||||
|
||||
**Core concept:** Build a systematic palette with 5-9 shades per color, add subtle saturation to grays, and design in grayscale first.
|
||||
|
||||
**Why it works:** Random colors clash. A systematic palette with predefined shades ensures consistency across the entire interface. HSL adjustments create natural-feeling lighter and darker variants.
|
||||
|
||||
**Key insights:**
|
||||
- Each color needs 5-9 shades from near-white to near-black (50 through 900)
|
||||
- The darkest shade is not black -- use 900-level dark grays (e.g., `#111827`) instead of pure `#000000`
|
||||
- Pure grays look lifeless -- add subtle saturation (cool UI: blue tint like `#64748b`; warm UI: yellow/brown tint like `#78716c`)
|
||||
- HSL adjustments: lighter = higher lightness, lower saturation, shift hue toward 60 degrees; darker = lower lightness, higher saturation, shift hue toward 0/240 degrees
|
||||
- Body text minimum 4.5:1 contrast ratio; large text (18px+) minimum 3:1
|
||||
- Use `#374151` (gray-700) on white, not lighter grays for readable text
|
||||
|
||||
**Product applications:**
|
||||
|
||||
| Context | Color Strategy | Example |
|
||||
|---------|---------------|---------|
|
||||
| **Primary palette** | 9 shades (50-900) for main brand color | Blue-500 for buttons, Blue-100 for backgrounds |
|
||||
| **Gray palette** | Saturated grays matching UI temperature | Cool grays with blue tint for tech products |
|
||||
| **Semantic colors** | Success, warning, error each with shade range | Green-500 for success, Red-500 for errors |
|
||||
| **Text colors** | Three levels: dark, medium, light | `text-gray-900`, `text-gray-600`, `text-gray-400` |
|
||||
| **Accessible contrast** | Test all text/background combos | `#374151` on white = 10.5:1 ratio |
|
||||
|
||||
**CSS patterns:**
|
||||
- `text-gray-900`(dark) `text-gray-600`(medium) `text-gray-400`(light)
|
||||
- `bg-blue-50` for subtle backgrounds, `bg-blue-500` for primary actions
|
||||
- `border-gray-200` for subtle borders, `border-gray-300` for stronger
|
||||
|
||||
**Ethical boundary:** Don't use color alone to convey information -- always pair with text or icons for accessibility.
|
||||
|
||||
See: [references/theming-dark-mode.md](references/theming-dark-mode.md) for dark palette creation and theme implementation.
|
||||
|
||||
### 5. Depth & Shadows
|
||||
|
||||
**Core concept:** Use a shadow scale to convey elevation. Small shadows for slightly raised elements, large shadows for floating elements.
|
||||
|
||||
**Why it works:** Shadows create a sense of physical depth that helps users understand which elements are interactive, which are floating above the surface, and which are part of the background.
|
||||
|
||||
**Key insights:**
|
||||
- Small shadows = raised slightly (buttons, cards); large shadows = floating (modals, dropdowns)
|
||||
- Shadows have two parts: a tight, dark shadow for crispness plus a larger, softer shadow for atmosphere
|
||||
- Depth without shadows: lighter top border + darker bottom border, subtle gradient backgrounds, overlapping elements with offset
|
||||
- Don't overuse shadows -- if everything floats, nothing has depth
|
||||
- Shadow color should be transparent dark, not opaque gray
|
||||
|
||||
**Product applications:**
|
||||
|
||||
| Context | Shadow Level | Example |
|
||||
|---------|-------------|---------|
|
||||
| **Buttons** | `shadow-sm` (subtle raise) | Slightly elevated above page surface |
|
||||
| **Cards** | `shadow-md` (clear separation) | Content grouped and lifted from background |
|
||||
| **Dropdowns** | `shadow-lg` (floating) | Menu clearly floating above content |
|
||||
| **Modals** | `shadow-xl` (highest elevation) | Overlay clearly detached from page |
|
||||
| **Flat alternatives** | Border + background shift | Lighter top border, darker bottom border |
|
||||
|
||||
**CSS patterns:**
|
||||
- `shadow-sm`: `0 1px 2px rgba(0,0,0,0.05)`
|
||||
- `shadow-md`: `0 4px 6px rgba(0,0,0,0.1)`
|
||||
- `shadow-lg`: `0 10px 15px rgba(0,0,0,0.1)`
|
||||
- `shadow-xl`: `0 20px 25px rgba(0,0,0,0.15)`
|
||||
|
||||
**Ethical boundary:** Don't use excessive shadows or visual emphasis to draw attention to deceptive UI elements (dark patterns).
|
||||
|
||||
See: [references/advanced-patterns.md](references/advanced-patterns.md) for interaction states and elevation hierarchy.
|
||||
|
||||
### 6. Images & Icons
|
||||
|
||||
**Core concept:** Treat images as design elements, not afterthoughts. Size icons deliberately and use overlays to ensure text readability on images.
|
||||
|
||||
**Why it works:** Poorly sized icons look awkward. Unstyled images break visual consistency. Deliberate image treatment (overlays, object-fit, border radius) makes interfaces feel polished.
|
||||
|
||||
**Key insights:**
|
||||
- Icons should be sized relative to their context -- don't use the same size everywhere
|
||||
- Use icon sets with consistent stroke width and style
|
||||
- Images need treatment: object-fit cover, consistent aspect ratios, overlays for text
|
||||
- Don't stretch or distort images -- use `object-fit: cover` and crop deliberately
|
||||
- Empty states are an opportunity -- use illustrations, not just text
|
||||
|
||||
**Product applications:**
|
||||
|
||||
| Context | Image/Icon Technique | Example |
|
||||
|---------|---------------------|---------|
|
||||
| **Hero images** | Overlay with semi-transparent gradient | Text readable over any photo |
|
||||
| **Avatars** | Consistent size, rounded, fallback initials | 40px circle with object-fit cover |
|
||||
| **Feature icons** | Consistent size, weight, and color | 24px stroke icons in gray-500 |
|
||||
| **Empty states** | Custom illustration + clear CTA | Friendly illustration with "Get started" button |
|
||||
| **Thumbnails** | Fixed aspect ratio with object-fit cover | 16:9 cards with no distortion |
|
||||
|
||||
**CSS patterns:**
|
||||
- `object-fit: cover` with fixed `aspect-ratio` for consistent image display
|
||||
- Icon sizing: `w-4 h-4` inline, `w-6 h-6` in navigation, `w-8 h-8` for feature icons
|
||||
- Image overlay: `bg-gradient-to-t from-black/60 to-transparent` for text on images
|
||||
|
||||
**Ethical boundary:** Don't use misleading images or icons that misrepresent functionality or product capabilities.
|
||||
|
||||
See: [references/advanced-patterns.md](references/advanced-patterns.md) for image treatment, icon usage, and empty states.
|
||||
|
||||
### 7. Layout & Composition
|
||||
|
||||
**Core concept:** Don't center everything. Use alignment, overlap, and emphasis variation to create engaging compositions.
|
||||
|
||||
**Why it works:** Left-aligned text is easier to read. Varied layouts keep users engaged. Breaking out of rigid boxes makes designs feel dynamic and intentional.
|
||||
|
||||
**Key insights:**
|
||||
- Left-align text by default; center only short headlines, hero sections, single-action CTAs, and empty states
|
||||
- Cards don't need to contain everything -- let images bleed to edges, overlap containers, or extend beyond bounds
|
||||
- In lists and feeds, vary the visual treatment -- feature some items, minimize others
|
||||
- Use alignment to create visual relationships between unrelated elements
|
||||
- Alternate emphasis: not every card in a list needs the same layout
|
||||
|
||||
**Product applications:**
|
||||
|
||||
| Context | Layout Strategy | Example |
|
||||
|---------|----------------|---------|
|
||||
| **Hero sections** | Centered text, generous spacing | Short headline + subtext + single CTA |
|
||||
| **Feature grids** | Left-aligned text, consistent card sizes | 3-column grid with icon + title + description |
|
||||
| **Blog feeds** | Varied card sizes for emphasis | First post large, next posts in 2-column grid |
|
||||
| **Sidebars** | Narrower than main content, lighter background | Navigation or filters at 240-320px width |
|
||||
| **Content pages** | Constrained width, left-aligned | `max-w-prose` centered container with left text |
|
||||
|
||||
**CSS patterns:**
|
||||
- `text-left` by default, `text-center` only for heroes and short headlines
|
||||
- `grid grid-cols-3 gap-6` for feature grids
|
||||
- `max-w-4xl mx-auto` for page containers
|
||||
- `overflow-hidden` on cards with `object-fit: cover` images that bleed to edges
|
||||
|
||||
**Ethical boundary:** Don't use layout tricks to hide or obscure important user choices like opt-outs or data permissions.
|
||||
|
||||
See: [references/advanced-patterns.md](references/advanced-patterns.md) for responsive breakpoints and complex layout patterns.
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
| Mistake | Why It Fails | Fix |
|
||||
|---------|-------------|------|
|
||||
| **"Looks amateur"** | Insufficient white space, unconstrained widths | Add more white space, constrain content widths |
|
||||
| **"Feels flat"** | No depth differentiation between elements | Add subtle shadows, border-bottom on sections |
|
||||
| **"Text is hard to read"** | Poor line-height, too wide, low contrast | Increase line-height, constrain width, boost contrast |
|
||||
| **"Everything looks the same"** | No visual hierarchy between elements | Vary size/weight/color between primary and secondary |
|
||||
| **"Feels cluttered"** | Equal spacing everywhere, no grouping | Group related items, increase spacing between groups |
|
||||
| **"Colors clash"** | Random color choices without a system | Reduce saturation, use more grays, limit palette to system |
|
||||
| **"Buttons don't pop"** | Low contrast with surrounding elements | Increase contrast with surroundings, add shadow |
|
||||
| **Using arbitrary values** | px values like 13, 17, 23 create inconsistency | Stick to the spacing and type scales |
|
||||
|
||||
## Quick Diagnostic
|
||||
|
||||
Audit any UI design:
|
||||
|
||||
| Question | If No | Action |
|
||||
|----------|-------|--------|
|
||||
| Does hierarchy read when squinting (blur test)? | Elements competing for attention | Increase contrast between primary and secondary |
|
||||
| Does it work in grayscale? | Relying on color for hierarchy | Strengthen size/weight/spacing hierarchy |
|
||||
| Is there enough white space? | Probably not -- most designs are too dense | Increase spacing, especially between groups |
|
||||
| Are labels de-emphasized vs. their values? | Labels competing with data | Make labels smaller, lighter, or uppercase-small |
|
||||
| Does spacing follow a consistent scale? | Arbitrary spacing creates visual noise | Use 4/8/16/24/32/48/64 scale only |
|
||||
| Is text width constrained for readability? | Long lines cause reader fatigue | Apply `max-w-prose` (~65ch) to text blocks |
|
||||
| Do colors have sufficient contrast? | Accessibility failure, hard to read | Test with WCAG contrast checker, use gray-700+ on white |
|
||||
| Are shadows appropriate for elevation? | Elements floating at wrong visual level | Match shadow scale to element purpose |
|
||||
|
||||
## Reference Files
|
||||
|
||||
- [advanced-patterns.md](references/advanced-patterns.md): Empty states, form design, image treatment, icon sizing, interaction states, color psychology, border radius systems, text truncation, responsive breakpoints
|
||||
- [animation-microinteractions.md](references/animation-microinteractions.md): When to animate, easing functions, durations, loading states, animation performance
|
||||
- [accessibility-depth.md](references/accessibility-depth.md): WCAG 2.1 AA checklist, focus management, screen reader support, keyboard navigation
|
||||
- [data-visualization.md](references/data-visualization.md): Chart selection, color in charts, table design, dashboard layouts
|
||||
- [theming-dark-mode.md](references/theming-dark-mode.md): Dark palette creation, elevation in dark mode, theme implementation strategies
|
||||
|
||||
## Further Reading
|
||||
|
||||
This skill is based on Adam Wathan and Steve Schoger's practical design guide. For the complete system with visual examples:
|
||||
|
||||
- [*"Refactoring UI"*](https://www.amazon.com/Refactoring-UI-Adam-Wathan/dp/B0BLJ7MC21?tag=wondelai00-20) by Adam Wathan & Steve Schoger (the full book with hundreds of visual before/after examples)
|
||||
- [*"The Design of Everyday Things"*](https://www.amazon.com/Design-Everyday-Things-Revised-Expanded/dp/0465050654?tag=wondelai00-20) by Don Norman (foundational design thinking and usability)
|
||||
- [*"Don't Make Me Think"*](https://www.amazon.com/Dont-Make-Think-Revisited-Usability/dp/0321965515?tag=wondelai00-20) by Steve Krug (web usability principles that complement Refactoring UI)
|
||||
- [Refactoring UI](https://www.refactoringui.com/) -- Official site with resources and examples
|
||||
|
||||
## About the Authors
|
||||
|
||||
**Adam Wathan** is a full-stack developer and the creator of Tailwind CSS, one of the most popular utility-first CSS frameworks. **Steve Schoger** is a visual designer known for his practical design tips and illustrations. Together they created *Refactoring UI* to teach developers how to design better interfaces using systematic, repeatable techniques rather than relying on innate artistic talent. Their approach emphasizes constrained design systems -- fixed scales for spacing, typography, color, and shadows -- that produce professional results without requiring a design background.
|
||||
@@ -0,0 +1,507 @@
|
||||
# Accessibility Deep Dive
|
||||
|
||||
Comprehensive WCAG 2.1 AA compliance checklist with practical implementation guidance.
|
||||
|
||||
## Accessibility Philosophy
|
||||
|
||||
Accessibility isn't just compliance—it improves UX for everyone:
|
||||
- Keyboard navigation helps power users
|
||||
- Good contrast helps in bright sunlight
|
||||
- Clear focus states help everyone understand what's selected
|
||||
- Proper headings help screen readers AND SEO
|
||||
|
||||
---
|
||||
|
||||
## WCAG 2.1 AA Checklist
|
||||
|
||||
### Perceivable
|
||||
|
||||
Users must be able to perceive content.
|
||||
|
||||
#### 1.1 Text Alternatives
|
||||
|
||||
**All images need alt text:**
|
||||
|
||||
```html
|
||||
<!-- Decorative image (no alt) -->
|
||||
<img src="divider.svg" alt="" role="presentation">
|
||||
|
||||
<!-- Informative image -->
|
||||
<img src="chart.png" alt="Sales increased 40% from Q1 to Q2">
|
||||
|
||||
<!-- Functional image (button/link) -->
|
||||
<button>
|
||||
<img src="search.svg" alt="Search">
|
||||
</button>
|
||||
|
||||
<!-- Complex image (needs long description) -->
|
||||
<figure>
|
||||
<img src="diagram.png" alt="System architecture overview" aria-describedby="diagram-desc">
|
||||
<figcaption id="diagram-desc">Detailed description of the system architecture...</figcaption>
|
||||
</figure>
|
||||
```
|
||||
|
||||
**Icons:**
|
||||
```html
|
||||
<!-- Icon with visible text (icon is decorative) -->
|
||||
<button>
|
||||
<svg aria-hidden="true">...</svg>
|
||||
<span>Settings</span>
|
||||
</button>
|
||||
|
||||
<!-- Icon-only button -->
|
||||
<button aria-label="Settings">
|
||||
<svg aria-hidden="true">...</svg>
|
||||
</button>
|
||||
```
|
||||
|
||||
#### 1.3 Adaptable
|
||||
|
||||
**Semantic HTML structure:**
|
||||
|
||||
```html
|
||||
<!-- Use semantic elements -->
|
||||
<header>...</header>
|
||||
<nav>...</nav>
|
||||
<main>
|
||||
<article>
|
||||
<h1>Page Title</h1>
|
||||
<section>
|
||||
<h2>Section Title</h2>
|
||||
</section>
|
||||
</article>
|
||||
</main>
|
||||
<footer>...</footer>
|
||||
|
||||
<!-- Not this -->
|
||||
<div class="header">...</div>
|
||||
<div class="nav">...</div>
|
||||
```
|
||||
|
||||
**Heading hierarchy:**
|
||||
- One `<h1>` per page
|
||||
- Don't skip levels (h1 → h3)
|
||||
- Headings describe content structure
|
||||
|
||||
**Form labels:**
|
||||
```html
|
||||
<!-- Explicit label -->
|
||||
<label for="email">Email</label>
|
||||
<input id="email" type="email">
|
||||
|
||||
<!-- Implicit label -->
|
||||
<label>
|
||||
Email
|
||||
<input type="email">
|
||||
</label>
|
||||
|
||||
<!-- Hidden label (for visual designs without labels) -->
|
||||
<label for="search" class="sr-only">Search</label>
|
||||
<input id="search" type="search" placeholder="Search...">
|
||||
```
|
||||
|
||||
#### 1.4 Distinguishable
|
||||
|
||||
**Color contrast requirements:**
|
||||
|
||||
| Content Type | Minimum Ratio | Tool |
|
||||
|--------------|---------------|------|
|
||||
| Normal text (<18px) | 4.5:1 | WebAIM Contrast Checker |
|
||||
| Large text (≥18px or ≥14px bold) | 3:1 | |
|
||||
| UI components & graphics | 3:1 | |
|
||||
|
||||
**Common contrast fixes:**
|
||||
|
||||
```css
|
||||
/* Too light - fails */
|
||||
.text-light { color: #9ca3af; } /* gray-400: 3.1:1 on white */
|
||||
|
||||
/* Passes AA */
|
||||
.text-muted { color: #6b7280; } /* gray-500: 4.6:1 on white */
|
||||
|
||||
/* Passes AAA */
|
||||
.text-strong { color: #374151; } /* gray-700: 9.1:1 on white */
|
||||
```
|
||||
|
||||
**Don't rely on color alone:**
|
||||
|
||||
```html
|
||||
<!-- Bad: only color indicates error -->
|
||||
<input class="border-red-500">
|
||||
|
||||
<!-- Good: color + icon + text -->
|
||||
<input class="border-red-500" aria-invalid="true" aria-describedby="error">
|
||||
<p id="error" class="text-red-600">
|
||||
<svg aria-hidden="true">⚠️</svg>
|
||||
Email is required
|
||||
</p>
|
||||
```
|
||||
|
||||
**Text resize:**
|
||||
- Content must be readable at 200% zoom
|
||||
- Use relative units (rem, em) not px for text
|
||||
- Test by zooming browser to 200%
|
||||
|
||||
---
|
||||
|
||||
### Operable
|
||||
|
||||
Users must be able to operate the interface.
|
||||
|
||||
#### 2.1 Keyboard Accessible
|
||||
|
||||
**All functionality must work with keyboard:**
|
||||
|
||||
| Key | Expected Behavior |
|
||||
|-----|-------------------|
|
||||
| Tab | Move to next focusable element |
|
||||
| Shift+Tab | Move to previous focusable element |
|
||||
| Enter | Activate links, buttons |
|
||||
| Space | Activate buttons, toggle checkboxes |
|
||||
| Arrows | Navigate within components (tabs, menus, radios) |
|
||||
| Escape | Close modals, dropdowns, cancel actions |
|
||||
|
||||
**Focus must be visible:**
|
||||
|
||||
```css
|
||||
/* Don't remove focus outlines */
|
||||
:focus {
|
||||
outline: none; /* ❌ Never do this without replacement */
|
||||
}
|
||||
|
||||
/* Do provide visible focus */
|
||||
:focus-visible {
|
||||
outline: 2px solid #3b82f6;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* Or use ring utility */
|
||||
.focusable:focus-visible {
|
||||
@apply ring-2 ring-blue-500 ring-offset-2;
|
||||
}
|
||||
```
|
||||
|
||||
**Keyboard traps:**
|
||||
- Modal dialogs should trap focus inside
|
||||
- But must have a way to exit (Escape key, close button)
|
||||
|
||||
```jsx
|
||||
// Focus trap for modals
|
||||
function Modal({ isOpen, onClose, children }) {
|
||||
const modalRef = useRef();
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
// Focus first focusable element
|
||||
const firstFocusable = modalRef.current.querySelector('button, input, a');
|
||||
firstFocusable?.focus();
|
||||
|
||||
// Trap focus inside
|
||||
const handleTab = (e) => {
|
||||
if (e.key === 'Tab') {
|
||||
// ... trap logic
|
||||
}
|
||||
if (e.key === 'Escape') {
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('keydown', handleTab);
|
||||
return () => document.removeEventListener('keydown', handleTab);
|
||||
}
|
||||
}, [isOpen]);
|
||||
|
||||
// Return focus on close
|
||||
}
|
||||
```
|
||||
|
||||
**Skip links:**
|
||||
|
||||
```html
|
||||
<body>
|
||||
<a href="#main-content" class="sr-only focus:not-sr-only">
|
||||
Skip to main content
|
||||
</a>
|
||||
<nav>...</nav>
|
||||
<main id="main-content">...</main>
|
||||
</body>
|
||||
```
|
||||
|
||||
#### 2.4 Navigable
|
||||
|
||||
**Page titles:**
|
||||
- Unique, descriptive page titles
|
||||
- Format: `Page Name | Site Name`
|
||||
|
||||
**Focus order:**
|
||||
- Must follow logical reading order
|
||||
- Don't use positive `tabindex` values (messes up order)
|
||||
- Only use `tabindex="0"` (make focusable) or `tabindex="-1"` (programmatically focusable)
|
||||
|
||||
**Link purpose:**
|
||||
```html
|
||||
<!-- Bad -->
|
||||
<a href="/article">Click here</a>
|
||||
<a href="/article">Read more</a>
|
||||
|
||||
<!-- Good -->
|
||||
<a href="/article">Read more about accessibility best practices</a>
|
||||
|
||||
<!-- Or with context -->
|
||||
<a href="/article" aria-describedby="article-title">Read more</a>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Understandable
|
||||
|
||||
Users must be able to understand content and operation.
|
||||
|
||||
#### 3.1 Readable
|
||||
|
||||
**Language declaration:**
|
||||
```html
|
||||
<html lang="en">
|
||||
<body>
|
||||
<p>This is English.</p>
|
||||
<p lang="fr">Ceci est français.</p>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
#### 3.2 Predictable
|
||||
|
||||
**Consistent navigation:**
|
||||
- Same navigation in same location across pages
|
||||
- Same elements behave the same way
|
||||
|
||||
**No unexpected changes:**
|
||||
- Form inputs don't auto-submit on change
|
||||
- No unexpected pop-ups
|
||||
- Focus doesn't move unexpectedly
|
||||
|
||||
```html
|
||||
<!-- Bad: changes page on select -->
|
||||
<select onchange="window.location = this.value">...</select>
|
||||
|
||||
<!-- Good: requires explicit action -->
|
||||
<select id="region">...</select>
|
||||
<button onclick="navigate()">Go</button>
|
||||
```
|
||||
|
||||
#### 3.3 Input Assistance
|
||||
|
||||
**Error identification:**
|
||||
```html
|
||||
<label for="email">Email</label>
|
||||
<input
|
||||
id="email"
|
||||
type="email"
|
||||
aria-invalid="true"
|
||||
aria-describedby="email-error"
|
||||
>
|
||||
<p id="email-error" class="error">
|
||||
Please enter a valid email address (e.g., name@example.com)
|
||||
</p>
|
||||
```
|
||||
|
||||
**Required fields:**
|
||||
```html
|
||||
<label for="name">
|
||||
Name <span aria-hidden="true">*</span>
|
||||
<span class="sr-only">(required)</span>
|
||||
</label>
|
||||
<input id="name" required aria-required="true">
|
||||
```
|
||||
|
||||
**Error prevention for critical actions:**
|
||||
- Confirm destructive actions
|
||||
- Allow review before submission
|
||||
- Provide undo capability
|
||||
|
||||
---
|
||||
|
||||
### Robust
|
||||
|
||||
Content must work with current and future technologies.
|
||||
|
||||
#### 4.1 Compatible
|
||||
|
||||
**Valid HTML:**
|
||||
- Unique IDs
|
||||
- Complete start/end tags
|
||||
- Proper nesting
|
||||
|
||||
**ARIA usage:**
|
||||
```html
|
||||
<!-- If you use ARIA, use it correctly -->
|
||||
|
||||
<!-- Roles -->
|
||||
<div role="button" tabindex="0" onclick="...">Fake Button</div>
|
||||
<!-- Better: just use <button> -->
|
||||
|
||||
<!-- States -->
|
||||
<button aria-pressed="true">Bold</button>
|
||||
<button aria-expanded="false" aria-controls="menu">Menu</button>
|
||||
|
||||
<!-- Live regions -->
|
||||
<div aria-live="polite" aria-atomic="true">
|
||||
<!-- Screen reader announces changes here -->
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Focus Management
|
||||
|
||||
### When to Manage Focus
|
||||
|
||||
| Scenario | Focus Action |
|
||||
|----------|--------------|
|
||||
| Modal opens | Focus first element inside modal |
|
||||
| Modal closes | Return focus to trigger element |
|
||||
| Error occurs | Focus error message or first invalid field |
|
||||
| New content loads | Focus heading or first new element |
|
||||
| Item deleted | Focus previous/next item or container |
|
||||
|
||||
### Implementation
|
||||
|
||||
```jsx
|
||||
// Store trigger reference
|
||||
const triggerRef = useRef();
|
||||
|
||||
function openModal() {
|
||||
triggerRef.current = document.activeElement;
|
||||
setIsOpen(true);
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
setIsOpen(false);
|
||||
// Return focus after state update
|
||||
setTimeout(() => triggerRef.current?.focus(), 0);
|
||||
}
|
||||
```
|
||||
|
||||
### Roving Tabindex (for component groups)
|
||||
|
||||
```jsx
|
||||
// Tab panels, menu items, radio groups
|
||||
function Tabs({ tabs }) {
|
||||
const [activeIndex, setActiveIndex] = useState(0);
|
||||
|
||||
const handleKeyDown = (e) => {
|
||||
if (e.key === 'ArrowRight') {
|
||||
setActiveIndex((activeIndex + 1) % tabs.length);
|
||||
}
|
||||
if (e.key === 'ArrowLeft') {
|
||||
setActiveIndex((activeIndex - 1 + tabs.length) % tabs.length);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div role="tablist" onKeyDown={handleKeyDown}>
|
||||
{tabs.map((tab, i) => (
|
||||
<button
|
||||
key={tab.id}
|
||||
role="tab"
|
||||
tabIndex={i === activeIndex ? 0 : -1}
|
||||
aria-selected={i === activeIndex}
|
||||
ref={i === activeIndex ? (el) => el?.focus() : null}
|
||||
>
|
||||
{tab.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Screen Reader Considerations
|
||||
|
||||
### Announce Dynamic Changes
|
||||
|
||||
```html
|
||||
<!-- Live region for status messages -->
|
||||
<div aria-live="polite" class="sr-only" id="status"></div>
|
||||
|
||||
<script>
|
||||
function showSuccess(message) {
|
||||
document.getElementById('status').textContent = message;
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
### Hide Decorative Content
|
||||
|
||||
```html
|
||||
<!-- Hidden from screen readers -->
|
||||
<svg aria-hidden="true">...</svg>
|
||||
<span aria-hidden="true">•</span>
|
||||
```
|
||||
|
||||
### Provide Context
|
||||
|
||||
```html
|
||||
<!-- Ambiguous button -->
|
||||
<button>Delete</button>
|
||||
|
||||
<!-- Clear button -->
|
||||
<button aria-label="Delete comment by John">Delete</button>
|
||||
|
||||
<!-- Or use aria-describedby -->
|
||||
<button aria-describedby="comment-123-author">Delete</button>
|
||||
<span id="comment-123-author" class="sr-only">comment by John</span>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing Tools
|
||||
|
||||
### Automated Testing
|
||||
|
||||
| Tool | What It Catches |
|
||||
|------|-----------------|
|
||||
| axe DevTools | ~30% of WCAG issues |
|
||||
| WAVE | Similar to axe, visual overlay |
|
||||
| Lighthouse | Basic accessibility audit |
|
||||
| ESLint a11y plugin | Catches issues in JSX |
|
||||
|
||||
### Manual Testing Required
|
||||
|
||||
Automated tools miss ~70% of issues. Manual testing needed for:
|
||||
- Keyboard navigation flow
|
||||
- Screen reader experience
|
||||
- Focus management
|
||||
- Meaningful alt text
|
||||
- Logical heading structure
|
||||
|
||||
### Testing Checklist
|
||||
|
||||
- [ ] Navigate entire page with keyboard only
|
||||
- [ ] Test with screen reader (VoiceOver, NVDA)
|
||||
- [ ] Check color contrast with tool
|
||||
- [ ] Zoom to 200% and verify usability
|
||||
- [ ] Test with high contrast mode
|
||||
- [ ] Verify focus indicators visible
|
||||
- [ ] Check heading structure with outline tool
|
||||
- [ ] Run axe DevTools audit
|
||||
- [ ] Test forms with validation errors
|
||||
|
||||
---
|
||||
|
||||
## Common Fixes Quick Reference
|
||||
|
||||
| Issue | Fix |
|
||||
|-------|-----|
|
||||
| Missing alt text | Add descriptive alt or `alt=""` for decorative |
|
||||
| Low contrast | Use gray-600+ for text on white |
|
||||
| Missing focus style | Add `focus-visible` ring/outline |
|
||||
| Click-only interaction | Add keyboard handler + focusability |
|
||||
| Missing form labels | Add `<label>` with `for` attribute |
|
||||
| Heading skip | Use h1→h2→h3 in order |
|
||||
| Color-only indicator | Add icon/text alongside color |
|
||||
| Modal focus trap | Trap focus, allow Escape to close |
|
||||
| Auto-playing media | Add pause control, don't autoplay |
|
||||
| Motion | Respect `prefers-reduced-motion` |
|
||||
@@ -0,0 +1,472 @@
|
||||
# Advanced Design Patterns
|
||||
|
||||
Extended reference for complex scenarios. Load only when deeper guidance needed.
|
||||
|
||||
## Table of Contents
|
||||
1. [Empty States](#empty-states)
|
||||
2. [Form Design](#form-design)
|
||||
3. [Image Treatment](#image-treatment)
|
||||
4. [Icon Usage](#icon-usage)
|
||||
5. [Interaction States](#interaction-states)
|
||||
6. [Color Psychology](#color-psychology)
|
||||
7. [Border Radius System](#border-radius-system)
|
||||
|
||||
---
|
||||
|
||||
## Empty States
|
||||
|
||||
Empty states are opportunities, not afterthoughts.
|
||||
|
||||
**Good empty states include:**
|
||||
- Illustration or icon (not generic)
|
||||
- Clear explanation of what goes here
|
||||
- Primary action to remedy emptiness
|
||||
- Optional secondary actions
|
||||
|
||||
```
|
||||
❌ "No items"
|
||||
✅ "No projects yet. Create your first project to get started."
|
||||
[+ Create Project]
|
||||
```
|
||||
|
||||
**Match the tone.** A todo app empty state can be playful; an enterprise dashboard should be professional.
|
||||
|
||||
---
|
||||
|
||||
## Form Design
|
||||
|
||||
### Input Sizing
|
||||
|
||||
Match input width to expected content:
|
||||
- Email/URL: Full width or ~400px
|
||||
- Phone: ~200px
|
||||
- ZIP code: ~100px
|
||||
- Street address: Full width
|
||||
- City: ~200px
|
||||
- State dropdown: ~150px
|
||||
|
||||
### Placeholder vs. Label
|
||||
|
||||
**Never use placeholder as the only label.** Placeholders disappear on focus. Always have a visible label.
|
||||
|
||||
Placeholders work for:
|
||||
- Format hints: "MM/DD/YYYY"
|
||||
- Examples: "e.g., john@example.com"
|
||||
- Optional clarification
|
||||
|
||||
### Input States
|
||||
|
||||
```
|
||||
Default: border-gray-300
|
||||
Focus: border-blue-500 ring-2 ring-blue-200
|
||||
Error: border-red-500 ring-2 ring-red-200
|
||||
Disabled: bg-gray-100 text-gray-400
|
||||
Success: border-green-500 (sparingly)
|
||||
```
|
||||
|
||||
### Button Hierarchy
|
||||
|
||||
One primary action per view. Everything else is secondary or tertiary.
|
||||
|
||||
```
|
||||
Primary: Solid color, high contrast (bg-blue-600 text-white)
|
||||
Secondary: Outlined or muted (border border-gray-300)
|
||||
Tertiary: Text only (text-blue-600 hover:underline)
|
||||
Danger: Red but not screaming (bg-red-600 for confirm, text-red-600 for trigger)
|
||||
```
|
||||
|
||||
### Form Layout
|
||||
|
||||
- One column for simple forms
|
||||
- Two columns ONLY when inputs are related (First/Last name, City/State)
|
||||
- Labels above inputs on mobile, beside on desktop (optional)
|
||||
- Group related fields with subtle boundaries or spacing
|
||||
|
||||
---
|
||||
|
||||
## Image Treatment
|
||||
|
||||
### Background Images
|
||||
|
||||
**Problem:** Text over images is often unreadable.
|
||||
|
||||
**Solutions:**
|
||||
1. Semi-transparent overlay: `bg-black/50`
|
||||
2. Gradient overlay: `bg-gradient-to-t from-black/80 to-transparent`
|
||||
3. Text shadow: `text-shadow: 0 2px 4px rgba(0,0,0,0.5)`
|
||||
4. Solid color box behind text
|
||||
5. Choose images with natural dark/simple areas for text
|
||||
|
||||
### User Avatars
|
||||
|
||||
- Always have a fallback (initials, generic icon)
|
||||
- Consistent size per context (32px list, 48px card, 96px profile)
|
||||
- Round for people, square with border-radius for companies/products
|
||||
- Border adds polish: `ring-2 ring-white` for overlapping avatars
|
||||
|
||||
### Hero Images
|
||||
|
||||
- Don't stretch—use `object-cover`
|
||||
- Consider `aspect-ratio` for consistency
|
||||
- Compress appropriately (WebP, quality 80%)
|
||||
|
||||
---
|
||||
|
||||
## Icon Usage
|
||||
|
||||
### Sizing
|
||||
|
||||
Icons should feel balanced with adjacent text:
|
||||
- 12-14px text: 16px icon
|
||||
- 16px text: 20px icon
|
||||
- 18-20px text: 24px icon
|
||||
|
||||
### Icon + Text Pairing
|
||||
|
||||
Always align icon center with text baseline or center. Add consistent gap (8px typical).
|
||||
|
||||
```html
|
||||
<span class="flex items-center gap-2">
|
||||
<IconSettings class="w-5 h-5" />
|
||||
<span>Settings</span>
|
||||
</span>
|
||||
```
|
||||
|
||||
### When to Use Icons
|
||||
|
||||
- Navigation items
|
||||
- Common actions (edit, delete, share)
|
||||
- Status indicators
|
||||
- Feature lists (with caution—don't overdo)
|
||||
|
||||
### When NOT to Use Icons
|
||||
|
||||
- Don't add icons just to fill space
|
||||
- Skip icons on buttons with clear text ("Submit", "Continue")
|
||||
- Avoid decorative-only icons that add no meaning
|
||||
|
||||
---
|
||||
|
||||
## Interaction States
|
||||
|
||||
Every interactive element needs visible state changes:
|
||||
|
||||
### Hover
|
||||
- Subtle background change
|
||||
- Slight shadow increase
|
||||
- Color shift (darken primary by 10%)
|
||||
|
||||
### Active/Pressed
|
||||
- Darker than hover
|
||||
- Slight scale down (`scale-95`)
|
||||
- Reduce shadow
|
||||
|
||||
### Focus
|
||||
- Obvious ring (critical for accessibility)
|
||||
- Don't rely on color alone
|
||||
- `focus-visible` for keyboard-only focus
|
||||
|
||||
### Loading
|
||||
- Disable interaction
|
||||
- Show spinner or skeleton
|
||||
- Maintain layout size (prevent shift)
|
||||
|
||||
```css
|
||||
/* Example button states */
|
||||
.btn {
|
||||
@apply bg-blue-600 hover:bg-blue-700 active:bg-blue-800;
|
||||
@apply focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2;
|
||||
@apply disabled:opacity-50 disabled:cursor-not-allowed;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Color Psychology
|
||||
|
||||
Use color purposefully:
|
||||
|
||||
| Color | Association | Use for |
|
||||
|-------|-------------|---------|
|
||||
| Blue | Trust, calm, professional | Primary actions, links, corporate |
|
||||
| Green | Success, growth, go | Success states, positive actions |
|
||||
| Red | Error, danger, urgency | Errors, destructive actions, alerts |
|
||||
| Yellow/Orange | Warning, attention | Warnings, highlights |
|
||||
| Purple | Premium, creative | Premium features, creative apps |
|
||||
| Gray | Neutral, professional | Text, backgrounds, borders |
|
||||
|
||||
### Avoid
|
||||
|
||||
- Red for non-destructive primary buttons
|
||||
- Green for errors (colorblind users)
|
||||
- Low-saturation colors for important actions
|
||||
- More than 3 accent colors per interface
|
||||
|
||||
---
|
||||
|
||||
## Border Radius System
|
||||
|
||||
Stay consistent. Pick a system:
|
||||
|
||||
**Sharp/Modern:**
|
||||
```
|
||||
none: 0
|
||||
sm: 2px
|
||||
md: 4px
|
||||
lg: 6px
|
||||
full: 9999px (pills/circles)
|
||||
```
|
||||
|
||||
**Soft/Friendly:**
|
||||
```
|
||||
none: 0
|
||||
sm: 4px
|
||||
md: 8px
|
||||
lg: 12px
|
||||
xl: 16px
|
||||
full: 9999px
|
||||
```
|
||||
|
||||
### Rules
|
||||
|
||||
- Nested elements: inner radius = outer radius - padding
|
||||
- Small elements get smaller radius (badges, tags)
|
||||
- Large elements can have larger radius (cards, modals)
|
||||
- Images inside cards: match card radius or use `overflow-hidden`
|
||||
|
||||
---
|
||||
|
||||
## Text Wrapping & Truncation
|
||||
|
||||
### When to Truncate
|
||||
|
||||
- Navigation items
|
||||
- Table cells with fixed widths
|
||||
- Card titles (with hover to reveal)
|
||||
|
||||
```css
|
||||
.truncate {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
```
|
||||
|
||||
### When NOT to Truncate
|
||||
|
||||
- Body text
|
||||
- Important information
|
||||
- Search results
|
||||
- Error messages
|
||||
|
||||
### Multi-line Truncation
|
||||
|
||||
```css
|
||||
.line-clamp-2 {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Responsive Breakpoints
|
||||
|
||||
Standard breakpoints (Tailwind default):
|
||||
|
||||
```
|
||||
sm: 640px (landscape phones)
|
||||
md: 768px (tablets)
|
||||
lg: 1024px (laptops)
|
||||
xl: 1280px (desktops)
|
||||
2xl: 1536px (large screens)
|
||||
```
|
||||
|
||||
### Mobile-First Principles
|
||||
|
||||
1. Design for mobile first, add complexity for larger screens
|
||||
2. Stack on mobile, side-by-side on desktop
|
||||
3. Full-width inputs on mobile, constrained on desktop
|
||||
4. Larger touch targets on mobile (44px minimum)
|
||||
5. Hide secondary navigation in hamburger on mobile
|
||||
|
||||
---
|
||||
|
||||
## Modal and Overlay Patterns
|
||||
|
||||
### Modal Sizing
|
||||
|
||||
| Content Type | Width | Height |
|
||||
|--------------|-------|--------|
|
||||
| Confirmation dialog | 400-500px | Auto (minimal) |
|
||||
| Form modal | 500-600px | Auto |
|
||||
| Content modal | 600-800px | 70-80vh max |
|
||||
| Full-screen modal | 100vw | 100vh |
|
||||
|
||||
### Modal Structure
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ Title ✕ Close │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Modal content here │
|
||||
│ │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ Cancel Primary Action│
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Backdrop
|
||||
|
||||
```css
|
||||
.backdrop {
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
/* Or with blur */
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
```
|
||||
|
||||
### Modal Transitions
|
||||
|
||||
```css
|
||||
/* Fade + Scale */
|
||||
.modal {
|
||||
opacity: 0;
|
||||
transform: scale(0.95);
|
||||
transition: opacity 200ms ease-out, transform 200ms ease-out;
|
||||
}
|
||||
.modal.open {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Dropdown and Menu Design
|
||||
|
||||
### Dropdown Positioning
|
||||
|
||||
- Default: Below trigger, left-aligned
|
||||
- Flip: Above if no space below
|
||||
- Constrain to viewport
|
||||
|
||||
```css
|
||||
.dropdown {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
margin-top: 4px;
|
||||
}
|
||||
```
|
||||
|
||||
### Menu Styling
|
||||
|
||||
```css
|
||||
.menu {
|
||||
min-width: 180px;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 10px 25px rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
padding: 8px 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.menu-item:hover {
|
||||
background: #f3f4f6;
|
||||
}
|
||||
|
||||
.menu-divider {
|
||||
height: 1px;
|
||||
background: #e5e7eb;
|
||||
margin: 4px 0;
|
||||
}
|
||||
```
|
||||
|
||||
### Menu Item Types
|
||||
|
||||
| Type | Visual |
|
||||
|------|--------|
|
||||
| Standard | Text label |
|
||||
| With icon | Icon + Label |
|
||||
| With shortcut | Label + Shortcut |
|
||||
| With description | Label + Description |
|
||||
| Destructive | Red text |
|
||||
| Disabled | Grayed out |
|
||||
|
||||
---
|
||||
|
||||
## Navigation Patterns
|
||||
|
||||
### Top Navigation
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Logo Nav Item Nav Item Nav Item CTA │
|
||||
└─────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
- Sticky on scroll (optional)
|
||||
- Collapse to hamburger on mobile
|
||||
- Clear active state
|
||||
|
||||
### Side Navigation
|
||||
|
||||
```
|
||||
┌──────────────┬──────────────────────────────────┐
|
||||
│ Logo │ │
|
||||
│ │ │
|
||||
│ Dashboard │ Main Content │
|
||||
│ Projects │ │
|
||||
│ Settings │ │
|
||||
│ │ │
|
||||
│ ───────── │ │
|
||||
│ Account │ │
|
||||
│ Logout │ │
|
||||
└──────────────┴──────────────────────────────────┘
|
||||
```
|
||||
|
||||
- Collapsible to icons on desktop
|
||||
- Full overlay on mobile
|
||||
- Group related items
|
||||
|
||||
### Breadcrumbs
|
||||
|
||||
```
|
||||
Home > Category > Subcategory > Current Page
|
||||
```
|
||||
|
||||
- Clickable except current page
|
||||
- Truncate middle items if too long
|
||||
- Show on detail/nested pages
|
||||
|
||||
### Tabs
|
||||
|
||||
```css
|
||||
.tabs {
|
||||
display: flex;
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
.tab {
|
||||
padding: 12px 16px;
|
||||
border-bottom: 2px solid transparent;
|
||||
}
|
||||
|
||||
.tab.active {
|
||||
border-bottom-color: #3b82f6;
|
||||
color: #3b82f6;
|
||||
}
|
||||
```
|
||||
|
||||
- Underline or pill style
|
||||
- Horizontal scroll on mobile if many tabs
|
||||
- Consider vertical tabs for settings
|
||||
@@ -0,0 +1,428 @@
|
||||
# Animation & Microinteractions
|
||||
|
||||
Guidelines for when and how to animate UI elements effectively.
|
||||
|
||||
## The Purpose of Animation
|
||||
|
||||
Animation should serve a function, not just look nice.
|
||||
|
||||
### Valid Reasons to Animate
|
||||
|
||||
| Purpose | Example |
|
||||
|---------|---------|
|
||||
| **Feedback** | Button press confirmation, form submission success |
|
||||
| **Orientation** | Showing where something came from or went to |
|
||||
| **Focus** | Drawing attention to important changes |
|
||||
| **Teaching** | Demonstrating how something works |
|
||||
| **Continuity** | Maintaining context during state changes |
|
||||
| **Delight** | Occasional surprise (use sparingly) |
|
||||
|
||||
### Invalid Reasons to Animate
|
||||
|
||||
- "It looks cool"
|
||||
- "The competition does it"
|
||||
- "To show off our skills"
|
||||
- Every state change
|
||||
- To hide slow performance
|
||||
|
||||
---
|
||||
|
||||
## Timing & Duration
|
||||
|
||||
### Duration Guidelines
|
||||
|
||||
| Animation Type | Duration | Rationale |
|
||||
|----------------|----------|-----------|
|
||||
| Micro-feedback (hover, press) | 100-150ms | Must feel instant |
|
||||
| Simple transitions (fade, slide) | 150-250ms | Noticeable but quick |
|
||||
| Complex transitions (modal, navigation) | 250-350ms | Need time to follow |
|
||||
| Entrances/Reveals | 200-400ms | Can be slightly longer |
|
||||
| Decorative/Emphasis | 300-500ms | Purpose is to be noticed |
|
||||
|
||||
### The 200ms Rule
|
||||
|
||||
Most UI animations should be around 200ms:
|
||||
- Faster than 100ms → Too fast to perceive
|
||||
- Slower than 400ms → Feels sluggish, interrupts flow
|
||||
|
||||
**Exception:** Loading and progress indicators can be slower because they represent real waiting.
|
||||
|
||||
### Duration by Distance
|
||||
|
||||
Longer travel distance = longer duration (but not proportionally).
|
||||
|
||||
```
|
||||
Small movement (8-16px): 100-150ms
|
||||
Medium movement (50-100px): 150-250ms
|
||||
Large movement (full screen): 250-350ms
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Easing Functions
|
||||
|
||||
Easing makes motion feel natural. Linear motion looks robotic.
|
||||
|
||||
### Common Easing Curves
|
||||
|
||||
| Easing | Use For | Feel |
|
||||
|--------|---------|------|
|
||||
| **ease-out** | Elements entering | Fast start, gentle stop |
|
||||
| **ease-in** | Elements leaving | Gentle start, fast exit |
|
||||
| **ease-in-out** | Elements moving within view | Smooth throughout |
|
||||
| **linear** | Progress indicators, opacity changes | Mechanical (intentional) |
|
||||
|
||||
### When to Use Each
|
||||
|
||||
**Ease-out (default for entrances):**
|
||||
```css
|
||||
transition: transform 200ms ease-out;
|
||||
```
|
||||
- Modals appearing
|
||||
- Notifications sliding in
|
||||
- Dropdowns opening
|
||||
- Tooltips appearing
|
||||
|
||||
**Ease-in (for exits):**
|
||||
```css
|
||||
transition: opacity 150ms ease-in;
|
||||
```
|
||||
- Modals dismissing
|
||||
- Elements fading out
|
||||
- Notifications leaving
|
||||
|
||||
**Ease-in-out (for on-screen movement):**
|
||||
```css
|
||||
transition: transform 250ms ease-in-out;
|
||||
```
|
||||
- Tab indicators sliding
|
||||
- Carousel transitions
|
||||
- Drawer/sidebar toggling
|
||||
|
||||
### Custom Cubic Bezier
|
||||
|
||||
For more personality, customize curves:
|
||||
|
||||
```css
|
||||
/* Snappy entrance */
|
||||
transition: transform 200ms cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
|
||||
/* Smooth overshoot */
|
||||
transition: transform 300ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Animation Patterns
|
||||
|
||||
### Button States
|
||||
|
||||
**Hover:**
|
||||
```css
|
||||
.btn {
|
||||
transition: background-color 100ms ease-out, transform 100ms ease-out;
|
||||
}
|
||||
.btn:hover {
|
||||
background-color: var(--btn-hover);
|
||||
}
|
||||
```
|
||||
|
||||
**Active/Pressed:**
|
||||
```css
|
||||
.btn:active {
|
||||
transform: scale(0.97);
|
||||
}
|
||||
```
|
||||
|
||||
**Loading state:**
|
||||
```css
|
||||
.btn.loading {
|
||||
opacity: 0.7;
|
||||
pointer-events: none;
|
||||
}
|
||||
.btn.loading .spinner {
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
```
|
||||
|
||||
### Modal Entrance/Exit
|
||||
|
||||
**Enter:**
|
||||
```css
|
||||
.modal {
|
||||
opacity: 0;
|
||||
transform: scale(0.95) translateY(-10px);
|
||||
transition: opacity 200ms ease-out, transform 200ms ease-out;
|
||||
}
|
||||
.modal.open {
|
||||
opacity: 1;
|
||||
transform: scale(1) translateY(0);
|
||||
}
|
||||
```
|
||||
|
||||
**Exit:**
|
||||
```css
|
||||
.modal.closing {
|
||||
opacity: 0;
|
||||
transform: scale(0.95);
|
||||
transition: opacity 150ms ease-in, transform 150ms ease-in;
|
||||
}
|
||||
```
|
||||
|
||||
### Dropdown/Menu
|
||||
|
||||
```css
|
||||
.dropdown {
|
||||
opacity: 0;
|
||||
transform: translateY(-8px);
|
||||
pointer-events: none;
|
||||
transition: opacity 150ms ease-out, transform 150ms ease-out;
|
||||
}
|
||||
.dropdown.open {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
pointer-events: auto;
|
||||
}
|
||||
```
|
||||
|
||||
### Toast/Notification
|
||||
|
||||
```css
|
||||
.toast {
|
||||
transform: translateX(100%);
|
||||
transition: transform 300ms ease-out;
|
||||
}
|
||||
.toast.visible {
|
||||
transform: translateX(0);
|
||||
}
|
||||
.toast.exiting {
|
||||
transform: translateX(100%);
|
||||
transition: transform 200ms ease-in;
|
||||
}
|
||||
```
|
||||
|
||||
### Skeleton Loading
|
||||
|
||||
```css
|
||||
.skeleton {
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
#f0f0f0 25%,
|
||||
#e0e0e0 50%,
|
||||
#f0f0f0 75%
|
||||
);
|
||||
background-size: 200% 100%;
|
||||
animation: shimmer 1.5s infinite;
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0% { background-position: 200% 0; }
|
||||
100% { background-position: -200% 0; }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Loading States
|
||||
|
||||
### Types of Loading Indicators
|
||||
|
||||
| Type | Use When | Example |
|
||||
|------|----------|---------|
|
||||
| **Spinner** | Unknown duration, short wait expected | Button submission |
|
||||
| **Progress bar** | Known progress, longer operations | File upload |
|
||||
| **Skeleton** | Loading content layout | Feed items |
|
||||
| **Pulse/Shimmer** | Refreshing existing content | Pull to refresh |
|
||||
|
||||
### Spinner Guidelines
|
||||
|
||||
- Don't show immediately (wait 300-500ms)
|
||||
- If wait < 1 second, spinner may not be needed
|
||||
- Position in context (where content will appear)
|
||||
- Provide cancel option for long operations
|
||||
|
||||
```jsx
|
||||
// Delay spinner to avoid flash for fast operations
|
||||
const [showSpinner, setShowSpinner] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (isLoading) {
|
||||
const timer = setTimeout(() => setShowSpinner(true), 400);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
setShowSpinner(false);
|
||||
}, [isLoading]);
|
||||
```
|
||||
|
||||
### Progress Bar Guidelines
|
||||
|
||||
- Show percentage when meaningful
|
||||
- Don't let it jump backwards
|
||||
- Consider indeterminate state if progress unknown
|
||||
- Complete to 100% before hiding
|
||||
|
||||
### Skeleton Screen Guidelines
|
||||
|
||||
- Match layout of actual content
|
||||
- Use consistent bone shapes
|
||||
- Animate subtly (shimmer, not bounce)
|
||||
- Replace with content immediately when loaded
|
||||
|
||||
---
|
||||
|
||||
## Microinteractions
|
||||
|
||||
Small animations that provide feedback and delight.
|
||||
|
||||
### Effective Microinteractions
|
||||
|
||||
**Toggle switches:**
|
||||
```css
|
||||
.toggle-thumb {
|
||||
transition: transform 150ms ease-out;
|
||||
}
|
||||
.toggle.on .toggle-thumb {
|
||||
transform: translateX(20px);
|
||||
}
|
||||
```
|
||||
|
||||
**Checkbox check:**
|
||||
```css
|
||||
.checkmark {
|
||||
stroke-dasharray: 20;
|
||||
stroke-dashoffset: 20;
|
||||
transition: stroke-dashoffset 200ms ease-out;
|
||||
}
|
||||
.checkbox.checked .checkmark {
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
```
|
||||
|
||||
**Like/Heart animation:**
|
||||
```css
|
||||
.heart {
|
||||
transform: scale(1);
|
||||
transition: transform 150ms ease-out;
|
||||
}
|
||||
.heart.liked {
|
||||
animation: pop 300ms ease-out;
|
||||
}
|
||||
@keyframes pop {
|
||||
0% { transform: scale(1); }
|
||||
50% { transform: scale(1.2); }
|
||||
100% { transform: scale(1); }
|
||||
}
|
||||
```
|
||||
|
||||
**Input focus:**
|
||||
```css
|
||||
.input {
|
||||
border-color: #ccc;
|
||||
transition: border-color 150ms ease-out, box-shadow 150ms ease-out;
|
||||
}
|
||||
.input:focus {
|
||||
border-color: #3b82f6;
|
||||
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2);
|
||||
}
|
||||
```
|
||||
|
||||
### When Microinteractions Help
|
||||
|
||||
- Confirming user action occurred
|
||||
- Showing state change clearly
|
||||
- Making interface feel responsive
|
||||
- Guiding attention to changes
|
||||
|
||||
### When to Skip
|
||||
|
||||
- Repetitive actions (every keystroke)
|
||||
- Performance-critical paths
|
||||
- Accessibility mode (respect reduce-motion)
|
||||
|
||||
---
|
||||
|
||||
## Accessibility Considerations
|
||||
|
||||
### Respect User Preferences
|
||||
|
||||
```css
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Provide Alternatives
|
||||
|
||||
- Don't rely on animation alone to convey information
|
||||
- Ensure state changes are visible without animation
|
||||
- Allow users to disable animations in app settings
|
||||
|
||||
### Avoid Problematic Animations
|
||||
|
||||
| Avoid | Reason |
|
||||
|-------|--------|
|
||||
| Flashing/strobing | Can trigger seizures |
|
||||
| Parallax scrolling | Causes motion sickness |
|
||||
| Auto-playing video | Distracting, accessibility |
|
||||
| Infinite loops | Drains attention, battery |
|
||||
|
||||
---
|
||||
|
||||
## Performance Guidelines
|
||||
|
||||
### GPU-Accelerated Properties
|
||||
|
||||
Animate these for smooth 60fps:
|
||||
- `transform` (translate, scale, rotate)
|
||||
- `opacity`
|
||||
|
||||
Avoid animating (causes reflow/repaint):
|
||||
- `width`, `height`
|
||||
- `top`, `left`, `right`, `bottom`
|
||||
- `margin`, `padding`
|
||||
- `border-width`
|
||||
- `font-size`
|
||||
|
||||
### Use will-change Sparingly
|
||||
|
||||
```css
|
||||
/* Only for elements about to animate */
|
||||
.modal {
|
||||
will-change: transform, opacity;
|
||||
}
|
||||
|
||||
/* Remove after animation */
|
||||
.modal.static {
|
||||
will-change: auto;
|
||||
}
|
||||
```
|
||||
|
||||
### Batch Animations
|
||||
|
||||
Start animations together, not staggered excessively:
|
||||
- 0-50ms stagger: feels cohesive
|
||||
- 100ms+ stagger: feels slow, sequential
|
||||
|
||||
### Test on Low-end Devices
|
||||
|
||||
What's smooth on your MacBook may stutter on a budget Android phone. Test on real devices or throttle CPU in DevTools.
|
||||
|
||||
---
|
||||
|
||||
## Animation Checklist
|
||||
|
||||
Before shipping an animation:
|
||||
|
||||
- [ ] Does it serve a purpose (not just decoration)?
|
||||
- [ ] Is duration appropriate for the action?
|
||||
- [ ] Does easing feel natural?
|
||||
- [ ] Does it work with `prefers-reduced-motion`?
|
||||
- [ ] Is it GPU-accelerated (transform/opacity)?
|
||||
- [ ] Does it perform well on low-end devices?
|
||||
- [ ] Can the interface function without it?
|
||||
@@ -0,0 +1,472 @@
|
||||
# Data Visualization Design
|
||||
|
||||
Practical guide to designing charts, tables, and data displays that communicate clearly.
|
||||
|
||||
## Chart Selection
|
||||
|
||||
### Choosing the Right Chart Type
|
||||
|
||||
| Data Type | Best Chart | Avoid |
|
||||
|-----------|-----------|-------|
|
||||
| Part-to-whole | Pie (≤5 slices), stacked bar | Pie with many slices |
|
||||
| Change over time | Line chart, area chart | Pie chart |
|
||||
| Comparison (few items) | Bar chart (horizontal or vertical) | Line chart |
|
||||
| Comparison (many items) | Horizontal bar chart | Vertical bars (labels hard) |
|
||||
| Distribution | Histogram, box plot | Pie chart |
|
||||
| Correlation | Scatter plot | Line chart |
|
||||
| Composition over time | Stacked area, stacked bar | Multiple pie charts |
|
||||
|
||||
### When NOT to Use a Chart
|
||||
|
||||
Sometimes a simple number is better:
|
||||
- Single data point → Big number display
|
||||
- Two numbers to compare → Side-by-side with % change
|
||||
- Very few data points → Table might be clearer
|
||||
|
||||
```
|
||||
❌ Pie chart with 2 slices (73% vs 27%)
|
||||
✅ "73% of users completed onboarding"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Bar Charts
|
||||
|
||||
### Orientation
|
||||
|
||||
**Vertical bars:** Best when comparing few categories with short labels
|
||||
**Horizontal bars:** Best when comparing many categories or labels are long
|
||||
|
||||
### Bar Chart Rules
|
||||
|
||||
1. **Always start at zero** - Truncated axes mislead
|
||||
2. **Order meaningfully** - By value (descending) or logical order (time, alphabet)
|
||||
3. **Space bars correctly** - Gap between bars = 50-100% of bar width
|
||||
4. **Label directly** - Put values on bars, not in legend
|
||||
5. **Limit categories** - 5-7 bars maximum; group others as "Other"
|
||||
|
||||
### Bar Styling
|
||||
|
||||
```css
|
||||
/* Bar appearance */
|
||||
.bar {
|
||||
fill: var(--primary);
|
||||
border-radius: 2px 2px 0 0; /* Slight rounding on top only */
|
||||
}
|
||||
|
||||
/* Hover state */
|
||||
.bar:hover {
|
||||
fill: var(--primary-dark);
|
||||
}
|
||||
|
||||
/* Spacing */
|
||||
.bar-gap: 8px;
|
||||
.bar-width: 32px;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Line Charts
|
||||
|
||||
### When to Use
|
||||
|
||||
- Continuous data over time
|
||||
- Trends matter more than individual values
|
||||
- Comparing multiple series over same time period
|
||||
|
||||
### Line Chart Rules
|
||||
|
||||
1. **Start Y-axis at zero** (usually) - Unless all values are in narrow range
|
||||
2. **Limit to 4-5 lines** - More becomes unreadable
|
||||
3. **Use distinct line styles** - Different colors, consider dashes for printing
|
||||
4. **Label lines directly** - Not just in legend
|
||||
5. **Highlight important points** - Mark significant events or thresholds
|
||||
|
||||
### Line Styling
|
||||
|
||||
```css
|
||||
/* Line appearance */
|
||||
.line {
|
||||
stroke-width: 2px;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
/* Data points */
|
||||
.data-point {
|
||||
r: 4px;
|
||||
fill: white;
|
||||
stroke-width: 2px;
|
||||
}
|
||||
|
||||
/* Only show points on hover or for few data points */
|
||||
.data-point { opacity: 0; }
|
||||
.line-group:hover .data-point { opacity: 1; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pie Charts
|
||||
|
||||
### When to Use (Rarely)
|
||||
|
||||
- Showing parts of a whole
|
||||
- 2-5 slices maximum
|
||||
- Exact values less important than proportions
|
||||
- Users understand percentages must sum to 100%
|
||||
|
||||
### When NOT to Use
|
||||
|
||||
- Comparing values (use bar chart)
|
||||
- More than 5 categories
|
||||
- Values that don't sum to 100%
|
||||
- Showing change over time
|
||||
|
||||
### Pie Chart Rules
|
||||
|
||||
1. **Limit slices** - Maximum 5; combine small values into "Other"
|
||||
2. **Order by size** - Largest slice starting at 12 o'clock
|
||||
3. **Label directly** - Put labels on or near slices, not in legend
|
||||
4. **Show percentages** - Values help interpretation
|
||||
5. **Consider donut** - Center can show total or key metric
|
||||
|
||||
### Alternative: Simple Numbers
|
||||
|
||||
Often clearer than a pie chart:
|
||||
```
|
||||
┌─────────────────────────────────┐
|
||||
│ 67% Mobile 23% Desktop 10% Tablet │
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Color in Data Visualization
|
||||
|
||||
### Categorical Colors
|
||||
|
||||
For different categories, use distinct hues:
|
||||
|
||||
```
|
||||
Blue: #3b82f6
|
||||
Orange: #f97316
|
||||
Green: #22c55e
|
||||
Purple: #8b5cf6
|
||||
Yellow: #eab308
|
||||
```
|
||||
|
||||
**Rules:**
|
||||
- Maximum 5-7 distinct colors
|
||||
- Avoid red/green together (colorblindness)
|
||||
- Test with colorblind simulator
|
||||
|
||||
### Sequential Colors
|
||||
|
||||
For continuous data (low to high), use single hue with varying lightness:
|
||||
|
||||
```
|
||||
Light: #dbeafe (low values)
|
||||
#93c5fd
|
||||
#60a5fa
|
||||
#3b82f6
|
||||
Dark: #1d4ed8 (high values)
|
||||
```
|
||||
|
||||
### Diverging Colors
|
||||
|
||||
For data with meaningful center (positive/negative, above/below average):
|
||||
|
||||
```
|
||||
Negative: #ef4444 (red)
|
||||
Neutral: #f3f4f6 (gray)
|
||||
Positive: #22c55e (green)
|
||||
```
|
||||
|
||||
### Colorblind-Safe Palettes
|
||||
|
||||
Test your palette with:
|
||||
- Coblis Color Blindness Simulator
|
||||
- Chrome DevTools rendering settings
|
||||
|
||||
**Safe combinations:**
|
||||
- Blue + Orange (deuteranopia safe)
|
||||
- Blue + Yellow
|
||||
- Purple + Green + Orange
|
||||
|
||||
---
|
||||
|
||||
## Tables
|
||||
|
||||
### When Tables Beat Charts
|
||||
|
||||
- Precise values matter
|
||||
- Users need to look up specific data
|
||||
- Multiple attributes per item
|
||||
- Data will be exported/copied
|
||||
|
||||
### Table Design Principles
|
||||
|
||||
**1. Align numbers right**
|
||||
```
|
||||
Revenue
|
||||
────────
|
||||
$1,234
|
||||
$12,456
|
||||
$123,789
|
||||
```
|
||||
|
||||
**2. Align text left**
|
||||
```
|
||||
Product Name
|
||||
────────────
|
||||
Widget A
|
||||
Premium Widget
|
||||
Widget Pro Max
|
||||
```
|
||||
|
||||
**3. Use consistent precision**
|
||||
```
|
||||
❌ $1234, $567.89, $2.5k
|
||||
✅ $1,234.00, $567.89, $2,500.00
|
||||
```
|
||||
|
||||
**4. Minimize borders**
|
||||
```css
|
||||
/* Light horizontal lines only */
|
||||
tr {
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
}
|
||||
/* No vertical borders, no heavy lines */
|
||||
```
|
||||
|
||||
**5. Generous padding**
|
||||
```css
|
||||
td {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
```
|
||||
|
||||
### Table Patterns
|
||||
|
||||
**Zebra striping:**
|
||||
```css
|
||||
tr:nth-child(even) {
|
||||
background-color: #f9fafb;
|
||||
}
|
||||
```
|
||||
|
||||
**Hover highlighting:**
|
||||
```css
|
||||
tr:hover {
|
||||
background-color: #f3f4f6;
|
||||
}
|
||||
```
|
||||
|
||||
**Sortable columns:**
|
||||
```html
|
||||
<th>
|
||||
Revenue
|
||||
<button aria-label="Sort by revenue">▼</button>
|
||||
</th>
|
||||
```
|
||||
|
||||
**Sticky headers:**
|
||||
```css
|
||||
thead th {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background: white;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Dashboard Layout
|
||||
|
||||
### Visual Hierarchy
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────┐
|
||||
│ KEY METRICS (big numbers) │
|
||||
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
|
||||
│ │ $45.2K │ │ 1,234 │ │ 78.3% │ │ 4.2 │ │
|
||||
│ │ Revenue │ │ Users │ │ Conv. │ │ Rating │ │
|
||||
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
|
||||
├──────────────────────────────────────────────────────┤
|
||||
│ PRIMARY CHART (most important trend) │
|
||||
│ ┌──────────────────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ [Line chart: Revenue over time] │ │
|
||||
│ │ │ │
|
||||
│ └──────────────────────────────────────────────┘ │
|
||||
├──────────────────────────┬───────────────────────────┤
|
||||
│ SUPPORTING CHARTS │ DETAIL TABLE │
|
||||
│ ┌──────────────────┐ │ ┌───────────────────────┐ │
|
||||
│ │ [Bar chart] │ │ │ Recent Transactions │ │
|
||||
│ └──────────────────┘ │ │ ... │ │
|
||||
│ ┌──────────────────┐ │ │ ... │ │
|
||||
│ │ [Donut chart] │ │ └───────────────────────┘ │
|
||||
│ └──────────────────┘ │ │
|
||||
└──────────────────────────┴───────────────────────────┘
|
||||
```
|
||||
|
||||
### Key Metric Cards
|
||||
|
||||
```html
|
||||
<div class="metric-card">
|
||||
<span class="label">Revenue</span>
|
||||
<span class="value">$45,234</span>
|
||||
<span class="change positive">↑ 12.3%</span>
|
||||
</div>
|
||||
```
|
||||
|
||||
```css
|
||||
.metric-card {
|
||||
padding: 20px;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 2rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.change.positive { color: #22c55e; }
|
||||
.change.negative { color: #ef4444; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Chart Annotations
|
||||
|
||||
### When to Annotate
|
||||
|
||||
- Explain anomalies
|
||||
- Mark significant events
|
||||
- Highlight thresholds
|
||||
- Call out key insights
|
||||
|
||||
### Annotation Patterns
|
||||
|
||||
**Event markers:**
|
||||
```
|
||||
╭─── Product launch
|
||||
Revenue ──────────/│\──────────────
|
||||
│
|
||||
```
|
||||
|
||||
**Threshold lines:**
|
||||
```
|
||||
Revenue ──────────────────────
|
||||
- - - - - Target: $50k - - - -
|
||||
──────────────────────────────
|
||||
```
|
||||
|
||||
**Callout boxes:**
|
||||
```
|
||||
┌──────────────────────────┐
|
||||
│ 📈 Best month ever! │
|
||||
│ +47% vs. last year │
|
||||
└─────────────┬────────────┘
|
||||
│
|
||||
▼
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Accessibility in Data Viz
|
||||
|
||||
### Color Considerations
|
||||
|
||||
- Don't rely on color alone
|
||||
- Add patterns, labels, or icons
|
||||
- Ensure sufficient contrast
|
||||
- Test with colorblind simulators
|
||||
|
||||
### Screen Reader Support
|
||||
|
||||
```html
|
||||
<figure role="img" aria-label="Bar chart showing monthly revenue">
|
||||
<figcaption class="sr-only">
|
||||
Revenue by month: January $10,000, February $12,000...
|
||||
</figcaption>
|
||||
<svg>...</svg>
|
||||
</figure>
|
||||
```
|
||||
|
||||
### Data Tables as Alternative
|
||||
|
||||
For complex charts, provide data table version:
|
||||
```html
|
||||
<details>
|
||||
<summary>View data as table</summary>
|
||||
<table>...</table>
|
||||
</details>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
| Mistake | Problem | Fix |
|
||||
|---------|---------|-----|
|
||||
| 3D charts | Distorts perception | Use flat 2D |
|
||||
| Dual Y-axes | Confusing comparisons | Separate charts |
|
||||
| Too many colors | Visual noise | Limit to 5-7 |
|
||||
| Missing zero | Misleading differences | Start at zero |
|
||||
| No labels | Hard to interpret | Label directly |
|
||||
| Decorative elements | Chartjunk distracts | Remove non-data ink |
|
||||
| Inconsistent scales | Misleading comparison | Match scales |
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Chart Decision Tree
|
||||
|
||||
```
|
||||
What do you want to show?
|
||||
│
|
||||
├─ Comparison → Bar chart
|
||||
│
|
||||
├─ Change over time → Line chart
|
||||
│
|
||||
├─ Part of whole → Pie (≤5) or stacked bar
|
||||
│
|
||||
├─ Distribution → Histogram
|
||||
│
|
||||
├─ Relationship → Scatter plot
|
||||
│
|
||||
└─ Single value → Big number
|
||||
```
|
||||
|
||||
### Minimum Viable Chart Styling
|
||||
|
||||
```css
|
||||
/* Axes */
|
||||
.axis line, .axis path {
|
||||
stroke: #e5e7eb;
|
||||
}
|
||||
.axis text {
|
||||
fill: #6b7280;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* Grid */
|
||||
.grid line {
|
||||
stroke: #f3f4f6;
|
||||
}
|
||||
|
||||
/* Data */
|
||||
.bar, .line, .point {
|
||||
fill: #3b82f6;
|
||||
stroke: #3b82f6;
|
||||
}
|
||||
|
||||
/* Labels */
|
||||
.label {
|
||||
fill: #374151;
|
||||
font-size: 11px;
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,446 @@
|
||||
# Theming & Dark Mode Design
|
||||
|
||||
Creating effective color themes and implementing dark mode correctly.
|
||||
|
||||
## Dark Mode Philosophy
|
||||
|
||||
Dark mode isn't just inverting colors—it requires deliberate design decisions to maintain usability, hierarchy, and aesthetics.
|
||||
|
||||
### Why Dark Mode Matters
|
||||
|
||||
- **User preference:** Many users prefer it
|
||||
- **Eye strain:** Reduces strain in low-light environments
|
||||
- **Battery life:** Saves power on OLED screens
|
||||
- **Accessibility:** Some users have photosensitivity
|
||||
- **Professional expectation:** Users expect modern apps to support it
|
||||
|
||||
---
|
||||
|
||||
## Dark Mode Color Principles
|
||||
|
||||
### Don't Just Invert
|
||||
|
||||
| Light Mode | Bad Dark Mode | Good Dark Mode |
|
||||
|------------|---------------|----------------|
|
||||
| White `#ffffff` | Black `#000000` | Dark gray `#18181b` |
|
||||
| Black text `#000000` | White text `#ffffff` | Off-white `#fafafa` |
|
||||
| Gray `#6b7280` | Gray `#6b7280` | Lighter gray `#a1a1aa` |
|
||||
|
||||
### Key Principles
|
||||
|
||||
**1. Use dark grays, not pure black**
|
||||
|
||||
Pure black (`#000000`) creates harsh contrast and "halation" (text appears to glow).
|
||||
|
||||
```css
|
||||
/* Background scale for dark mode */
|
||||
--bg-base: #09090b; /* Deepest background */
|
||||
--bg-subtle: #18181b; /* Cards, elevated surfaces */
|
||||
--bg-muted: #27272a; /* Hover states, inputs */
|
||||
--bg-emphasis: #3f3f46; /* Active states */
|
||||
```
|
||||
|
||||
**2. Reduce contrast slightly**
|
||||
|
||||
Max contrast in dark mode is harsher than in light mode.
|
||||
|
||||
```css
|
||||
/* Text colors for dark mode */
|
||||
--text-primary: #fafafa; /* ~95% white, not 100% */
|
||||
--text-secondary: #a1a1aa; /* Muted text */
|
||||
--text-tertiary: #71717a; /* Subtle text */
|
||||
```
|
||||
|
||||
**3. Desaturate colors**
|
||||
|
||||
Bright saturated colors on dark backgrounds cause eye strain.
|
||||
|
||||
```css
|
||||
/* Light mode brand color */
|
||||
--primary-light: #3b82f6; /* Bright blue */
|
||||
|
||||
/* Dark mode - slightly desaturated */
|
||||
--primary-dark: #60a5fa; /* Lighter, less saturated */
|
||||
```
|
||||
|
||||
**4. Elevate with lightness, not shadow**
|
||||
|
||||
In dark mode, shadows are invisible. Show elevation with lighter surfaces.
|
||||
|
||||
```css
|
||||
/* Light mode: shadow for depth */
|
||||
.card-light {
|
||||
background: white;
|
||||
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
/* Dark mode: lighter surface for depth */
|
||||
.card-dark {
|
||||
background: #27272a; /* Lighter than base */
|
||||
box-shadow: none; /* Or very subtle */
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Building a Dark Mode Palette
|
||||
|
||||
### Step 1: Define Your Gray Scale
|
||||
|
||||
Create 9-10 shades from near-black to near-white:
|
||||
|
||||
```css
|
||||
/* Dark mode gray scale (Zinc example) */
|
||||
--gray-950: #09090b; /* Deepest background */
|
||||
--gray-900: #18181b; /* Card backgrounds */
|
||||
--gray-800: #27272a; /* Elevated surfaces */
|
||||
--gray-700: #3f3f46; /* Borders, dividers */
|
||||
--gray-600: #52525b; /* Disabled states */
|
||||
--gray-500: #71717a; /* Placeholder text */
|
||||
--gray-400: #a1a1aa; /* Secondary text */
|
||||
--gray-300: #d4d4d8; /* Primary text (alt) */
|
||||
--gray-200: #e4e4e7; /* Headings */
|
||||
--gray-100: #f4f4f5; /* Emphasis text */
|
||||
--gray-50: #fafafa; /* Primary text */
|
||||
```
|
||||
|
||||
### Step 2: Adjust Accent Colors
|
||||
|
||||
```css
|
||||
/* Primary color adjustments */
|
||||
/* Light mode: use 500-600 range */
|
||||
--primary-light: #2563eb;
|
||||
|
||||
/* Dark mode: use 400-500 range (lighter) */
|
||||
--primary-dark: #3b82f6;
|
||||
|
||||
/* Same for semantic colors */
|
||||
--success-light: #16a34a;
|
||||
--success-dark: #22c55e;
|
||||
|
||||
--error-light: #dc2626;
|
||||
--error-dark: #ef4444;
|
||||
```
|
||||
|
||||
### Step 3: Define Semantic Tokens
|
||||
|
||||
```css
|
||||
/* Semantic tokens that switch based on mode */
|
||||
:root {
|
||||
--color-bg: var(--gray-50);
|
||||
--color-bg-subtle: var(--gray-100);
|
||||
--color-text: var(--gray-900);
|
||||
--color-text-muted: var(--gray-600);
|
||||
--color-border: var(--gray-200);
|
||||
--color-primary: var(--blue-600);
|
||||
}
|
||||
|
||||
[data-theme="dark"] {
|
||||
--color-bg: var(--gray-950);
|
||||
--color-bg-subtle: var(--gray-900);
|
||||
--color-text: var(--gray-50);
|
||||
--color-text-muted: var(--gray-400);
|
||||
--color-border: var(--gray-800);
|
||||
--color-primary: var(--blue-400);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Strategies
|
||||
|
||||
### Strategy 1: CSS Custom Properties
|
||||
|
||||
```css
|
||||
:root {
|
||||
--bg: #ffffff;
|
||||
--text: #18181b;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--bg: #18181b;
|
||||
--text: #fafafa;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
}
|
||||
```
|
||||
|
||||
### Strategy 2: Data Attribute + Class
|
||||
|
||||
```html
|
||||
<html data-theme="dark">
|
||||
```
|
||||
|
||||
```css
|
||||
[data-theme="light"] {
|
||||
--bg: #ffffff;
|
||||
}
|
||||
[data-theme="dark"] {
|
||||
--bg: #18181b;
|
||||
}
|
||||
```
|
||||
|
||||
```javascript
|
||||
// Toggle theme
|
||||
function toggleTheme() {
|
||||
const current = document.documentElement.dataset.theme;
|
||||
document.documentElement.dataset.theme = current === 'dark' ? 'light' : 'dark';
|
||||
localStorage.setItem('theme', document.documentElement.dataset.theme);
|
||||
}
|
||||
|
||||
// Initialize from preference
|
||||
function initTheme() {
|
||||
const saved = localStorage.getItem('theme');
|
||||
const preferred = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||
document.documentElement.dataset.theme = saved || preferred;
|
||||
}
|
||||
```
|
||||
|
||||
### Strategy 3: Tailwind Dark Mode
|
||||
|
||||
```html
|
||||
<!-- With class strategy -->
|
||||
<html class="dark">
|
||||
<body class="bg-white dark:bg-zinc-950 text-zinc-900 dark:text-zinc-50">
|
||||
```
|
||||
|
||||
```javascript
|
||||
// tailwind.config.js
|
||||
module.exports = {
|
||||
darkMode: 'class', // or 'media' for system preference only
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Component Considerations
|
||||
|
||||
### Cards and Surfaces
|
||||
|
||||
```css
|
||||
/* Light: white with shadow */
|
||||
.card {
|
||||
background: white;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
/* Dark: lighter surface, subtle or no shadow */
|
||||
[data-theme="dark"] .card {
|
||||
background: var(--gray-900);
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.3); /* Darker shadow if any */
|
||||
/* Or: border: 1px solid var(--gray-800); */
|
||||
}
|
||||
```
|
||||
|
||||
### Form Inputs
|
||||
|
||||
```css
|
||||
.input {
|
||||
background: white;
|
||||
border: 1px solid var(--gray-300);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .input {
|
||||
background: var(--gray-900);
|
||||
border: 1px solid var(--gray-700);
|
||||
}
|
||||
```
|
||||
|
||||
### Buttons
|
||||
|
||||
```css
|
||||
/* Primary button */
|
||||
.btn-primary {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .btn-primary {
|
||||
/* Often same or slightly adjusted */
|
||||
background: var(--primary-dark);
|
||||
}
|
||||
|
||||
/* Secondary button */
|
||||
.btn-secondary {
|
||||
background: var(--gray-100);
|
||||
color: var(--gray-900);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .btn-secondary {
|
||||
background: var(--gray-800);
|
||||
color: var(--gray-100);
|
||||
}
|
||||
```
|
||||
|
||||
### Images and Media
|
||||
|
||||
```css
|
||||
/* Reduce brightness/contrast of images in dark mode */
|
||||
[data-theme="dark"] img:not([data-no-dim]) {
|
||||
filter: brightness(0.9) contrast(1.1);
|
||||
}
|
||||
|
||||
/* Invert diagrams/illustrations if needed */
|
||||
[data-theme="dark"] .diagram {
|
||||
filter: invert(1) hue-rotate(180deg);
|
||||
}
|
||||
```
|
||||
|
||||
### Syntax Highlighting
|
||||
|
||||
Don't forget code blocks need dark mode variants:
|
||||
- Use dark theme variants of syntax highlighters
|
||||
- Or invert colors appropriately
|
||||
- Popular: One Dark, Dracula, Night Owl
|
||||
|
||||
---
|
||||
|
||||
## Testing Dark Mode
|
||||
|
||||
### Checklist
|
||||
|
||||
- [ ] All text is readable (sufficient contrast)
|
||||
- [ ] Hierarchy still clear (headings vs body)
|
||||
- [ ] Focus states visible
|
||||
- [ ] Images don't blow out
|
||||
- [ ] Forms inputs clearly visible
|
||||
- [ ] Error/success states distinct
|
||||
- [ ] Loading states visible
|
||||
- [ ] Shadows/elevation still work
|
||||
- [ ] Icons visible (may need color swap)
|
||||
- [ ] Brand colors still recognizable
|
||||
|
||||
### Contrast Ratios
|
||||
|
||||
Same WCAG requirements apply:
|
||||
- Normal text: 4.5:1 minimum
|
||||
- Large text: 3:1 minimum
|
||||
- UI components: 3:1 minimum
|
||||
|
||||
**Common dark mode fails:**
|
||||
- Gray text on dark background
|
||||
- Colored text on colored backgrounds
|
||||
- Disabled states too subtle
|
||||
|
||||
---
|
||||
|
||||
## Theme Toggle UI
|
||||
|
||||
### Placement
|
||||
|
||||
- Header/navigation (most common)
|
||||
- Settings page
|
||||
- Footer (less common)
|
||||
|
||||
### Icon Patterns
|
||||
|
||||
```html
|
||||
<!-- Sun/Moon toggle -->
|
||||
<button aria-label="Toggle dark mode">
|
||||
<svg class="sun hidden dark:block">...</svg>
|
||||
<svg class="moon block dark:hidden">...</svg>
|
||||
</button>
|
||||
```
|
||||
|
||||
### State Options
|
||||
|
||||
1. **Light / Dark** - Simple toggle
|
||||
2. **Light / Dark / System** - Respect OS preference option
|
||||
3. **Auto only** - Always follow system (no toggle)
|
||||
|
||||
### Persistence
|
||||
|
||||
```javascript
|
||||
// Save preference
|
||||
localStorage.setItem('theme', 'dark');
|
||||
|
||||
// Load preference (with system fallback)
|
||||
const theme = localStorage.getItem('theme') ||
|
||||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Advanced: Multiple Themes
|
||||
|
||||
### Brand Themes
|
||||
|
||||
```css
|
||||
[data-theme="brand-a"] {
|
||||
--primary: #ff6b6b;
|
||||
--primary-hover: #ee5a5a;
|
||||
}
|
||||
|
||||
[data-theme="brand-b"] {
|
||||
--primary: #4ecdc4;
|
||||
--primary-hover: #3dbdb5;
|
||||
}
|
||||
```
|
||||
|
||||
### Theme Structure
|
||||
|
||||
```css
|
||||
/* Base tokens (don't change) */
|
||||
--spacing-4: 16px;
|
||||
--radius-md: 8px;
|
||||
|
||||
/* Color tokens (change per theme) */
|
||||
--color-primary: ...;
|
||||
--color-bg: ...;
|
||||
|
||||
/* Component tokens (reference color tokens) */
|
||||
--button-bg: var(--color-primary);
|
||||
--card-bg: var(--color-bg);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
| Mistake | Problem | Fix |
|
||||
|---------|---------|-----|
|
||||
| Pure black background | Harsh, looks flat | Use dark gray (#18181b) |
|
||||
| Pure white text | Too much contrast | Use off-white (#fafafa) |
|
||||
| Same saturated colors | Eye strain | Desaturate for dark mode |
|
||||
| Shadows for elevation | Invisible in dark | Use lighter surfaces |
|
||||
| Forgetting images | Can be too bright | Dim images slightly |
|
||||
| One contrast check | Colors interact differently | Check all combinations |
|
||||
| Forgetting focus states | Invisible borders | Ensure visible focus rings |
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Minimum Viable Dark Mode
|
||||
|
||||
```css
|
||||
:root {
|
||||
--bg: #ffffff;
|
||||
--bg-subtle: #f4f4f5;
|
||||
--text: #18181b;
|
||||
--text-muted: #71717a;
|
||||
--border: #e4e4e7;
|
||||
--primary: #2563eb;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--bg: #18181b;
|
||||
--bg-subtle: #27272a;
|
||||
--text: #fafafa;
|
||||
--text-muted: #a1a1aa;
|
||||
--border: #3f3f46;
|
||||
--primary: #3b82f6;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,257 @@
|
||||
# UX Heuristic Audit Template
|
||||
|
||||
Structured template for conducting heuristic evaluations. Use this to systematically audit any interface.
|
||||
|
||||
---
|
||||
|
||||
## Audit Information
|
||||
|
||||
**Product/Feature:** ________________
|
||||
**Auditor:** ________________
|
||||
**Date:** ________________
|
||||
**Page/Screen:** ________________
|
||||
**User Context:** ________________ (who is using this, what's their goal?)
|
||||
|
||||
---
|
||||
|
||||
## Severity Scale Reference
|
||||
|
||||
| Rating | Severity | Priority | Description |
|
||||
|--------|----------|----------|-------------|
|
||||
| 0 | Not a problem | — | Cosmetic or disagreement |
|
||||
| 1 | Cosmetic | Low | Minor annoyance |
|
||||
| 2 | Minor | Medium | Delays or frustrates |
|
||||
| 3 | Major | High | Significant task failure |
|
||||
| 4 | Catastrophic | Critical | Prevents task completion |
|
||||
|
||||
---
|
||||
|
||||
## Heuristic Checklist
|
||||
|
||||
### 1. Visibility of System Status
|
||||
|
||||
| Check | Pass? | Issue | Severity |
|
||||
|-------|-------|-------|----------|
|
||||
| Loading states are shown | [ ] | | |
|
||||
| Actions provide immediate feedback | [ ] | | |
|
||||
| Errors are communicated clearly | [ ] | | |
|
||||
| Success is confirmed | [ ] | | |
|
||||
| Progress is visible for long tasks | [ ] | | |
|
||||
|
||||
**Notes:**
|
||||
|
||||
---
|
||||
|
||||
### 2. Match Between System and Real World
|
||||
|
||||
| Check | Pass? | Issue | Severity |
|
||||
|-------|-------|-------|----------|
|
||||
| Uses user's language (not jargon) | [ ] | | |
|
||||
| Icons are recognizable | [ ] | | |
|
||||
| Order follows real-world logic | [ ] | | |
|
||||
| Metaphors are appropriate | [ ] | | |
|
||||
| Terms are consistent with industry | [ ] | | |
|
||||
|
||||
**Notes:**
|
||||
|
||||
---
|
||||
|
||||
### 3. User Control and Freedom
|
||||
|
||||
| Check | Pass? | Issue | Severity |
|
||||
|-------|-------|-------|----------|
|
||||
| Undo is available | [ ] | | |
|
||||
| Cancel/Exit is always accessible | [ ] | | |
|
||||
| Back button works as expected | [ ] | | |
|
||||
| Users can skip optional steps | [ ] | | |
|
||||
| Modals have clear close options | [ ] | | |
|
||||
|
||||
**Notes:**
|
||||
|
||||
---
|
||||
|
||||
### 4. Consistency and Standards
|
||||
|
||||
| Check | Pass? | Issue | Severity |
|
||||
|-------|-------|-------|----------|
|
||||
| Same terms for same concepts | [ ] | | |
|
||||
| Buttons/links styled consistently | [ ] | | |
|
||||
| Follows platform conventions | [ ] | | |
|
||||
| Layout is predictable | [ ] | | |
|
||||
| Same actions work the same way | [ ] | | |
|
||||
|
||||
**Notes:**
|
||||
|
||||
---
|
||||
|
||||
### 5. Error Prevention
|
||||
|
||||
| Check | Pass? | Issue | Severity |
|
||||
|-------|-------|-------|----------|
|
||||
| Destructive actions require confirmation | [ ] | | |
|
||||
| Inline validation prevents bad submissions | [ ] | | |
|
||||
| Constrained inputs prevent errors | [ ] | | |
|
||||
| Defaults are sensible | [ ] | | |
|
||||
| "Unsaved changes" warning exists | [ ] | | |
|
||||
|
||||
**Notes:**
|
||||
|
||||
---
|
||||
|
||||
### 6. Recognition Rather Than Recall
|
||||
|
||||
| Check | Pass? | Issue | Severity |
|
||||
|-------|-------|-------|----------|
|
||||
| Options are visible (not hidden) | [ ] | | |
|
||||
| Recent items/history is accessible | [ ] | | |
|
||||
| Icons have labels | [ ] | | |
|
||||
| Instructions are in context | [ ] | | |
|
||||
| Previous input is remembered | [ ] | | |
|
||||
|
||||
**Notes:**
|
||||
|
||||
---
|
||||
|
||||
### 7. Flexibility and Efficiency
|
||||
|
||||
| Check | Pass? | Issue | Severity |
|
||||
|-------|-------|-------|----------|
|
||||
| Keyboard shortcuts for power users | [ ] | | |
|
||||
| Bulk actions available | [ ] | | |
|
||||
| Customization options exist | [ ] | | |
|
||||
| Frequent tasks are quick | [ ] | | |
|
||||
| Advanced features don't clutter basics | [ ] | | |
|
||||
|
||||
**Notes:**
|
||||
|
||||
---
|
||||
|
||||
### 8. Aesthetic and Minimalist Design
|
||||
|
||||
| Check | Pass? | Issue | Severity |
|
||||
|-------|-------|-------|----------|
|
||||
| Only essential info is shown | [ ] | | |
|
||||
| Visual hierarchy is clear | [ ] | | |
|
||||
| Whitespace is used effectively | [ ] | | |
|
||||
| No unnecessary decoration | [ ] | | |
|
||||
| Primary action is obvious | [ ] | | |
|
||||
|
||||
**Notes:**
|
||||
|
||||
---
|
||||
|
||||
### 9. Error Recovery
|
||||
|
||||
| Check | Pass? | Issue | Severity |
|
||||
|-------|-------|-------|----------|
|
||||
| Error messages are in plain language | [ ] | | |
|
||||
| Errors explain what went wrong | [ ] | | |
|
||||
| Errors suggest how to fix | [ ] | | |
|
||||
| User input is preserved on error | [ ] | | |
|
||||
| Error location is highlighted | [ ] | | |
|
||||
|
||||
**Notes:**
|
||||
|
||||
---
|
||||
|
||||
### 10. Help and Documentation
|
||||
|
||||
| Check | Pass? | Issue | Severity |
|
||||
|-------|-------|-------|----------|
|
||||
| Help is searchable | [ ] | | |
|
||||
| Contextual help is available | [ ] | | |
|
||||
| Documentation is task-focused | [ ] | | |
|
||||
| Tooltips explain non-obvious features | [ ] | | |
|
||||
| Support is accessible | [ ] | | |
|
||||
|
||||
**Notes:**
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
### Issue Count by Severity
|
||||
|
||||
| Severity | Count |
|
||||
|----------|-------|
|
||||
| 4 (Catastrophic) | |
|
||||
| 3 (Major) | |
|
||||
| 2 (Minor) | |
|
||||
| 1 (Cosmetic) | |
|
||||
|
||||
### Top Issues (Severity 3-4)
|
||||
|
||||
| # | Issue | Heuristic | Severity | Recommendation |
|
||||
|---|-------|-----------|----------|----------------|
|
||||
| 1 | | | | |
|
||||
| 2 | | | | |
|
||||
| 3 | | | | |
|
||||
| 4 | | | | |
|
||||
| 5 | | | | |
|
||||
|
||||
### Quick Wins (Low effort, high impact)
|
||||
|
||||
| Issue | Fix | Effort |
|
||||
|-------|-----|--------|
|
||||
| | | |
|
||||
| | | |
|
||||
| | | |
|
||||
|
||||
---
|
||||
|
||||
## Task-Based Audit (Optional)
|
||||
|
||||
Test specific user tasks and note where issues occur.
|
||||
|
||||
### Task 1: ________________
|
||||
|
||||
| Step | Expected | Actual | Issue | Severity |
|
||||
|------|----------|--------|-------|----------|
|
||||
| 1 | | | | |
|
||||
| 2 | | | | |
|
||||
| 3 | | | | |
|
||||
|
||||
### Task 2: ________________
|
||||
|
||||
| Step | Expected | Actual | Issue | Severity |
|
||||
|------|----------|--------|-------|----------|
|
||||
| 1 | | | | |
|
||||
| 2 | | | | |
|
||||
| 3 | | | | |
|
||||
|
||||
---
|
||||
|
||||
## Navigation/Trunk Test
|
||||
|
||||
Can users answer these questions immediately?
|
||||
|
||||
| Question | Can Answer? | Notes |
|
||||
|----------|-------------|-------|
|
||||
| What site is this? | [ ] Yes [ ] No | |
|
||||
| What page am I on? | [ ] Yes [ ] No | |
|
||||
| What are the major sections? | [ ] Yes [ ] No | |
|
||||
| What are my options here? | [ ] Yes [ ] No | |
|
||||
| Where am I in the site structure? | [ ] Yes [ ] No | |
|
||||
| How can I search? | [ ] Yes [ ] No | |
|
||||
|
||||
---
|
||||
|
||||
## Recommendations Summary
|
||||
|
||||
### Immediate (Severity 4)
|
||||
1.
|
||||
2.
|
||||
|
||||
### Short-term (Severity 3)
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
### Medium-term (Severity 2)
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
### Low Priority (Severity 1)
|
||||
1.
|
||||
2.
|
||||
@@ -0,0 +1,346 @@
|
||||
# Cultural UX Considerations
|
||||
|
||||
Designing for global audiences: RTL languages, color meanings, form conventions, and localization.
|
||||
|
||||
## Right-to-Left (RTL) Languages
|
||||
|
||||
### Languages That Use RTL
|
||||
|
||||
- Arabic
|
||||
- Hebrew
|
||||
- Persian (Farsi)
|
||||
- Urdu
|
||||
|
||||
### What to Mirror
|
||||
|
||||
| Element | LTR | RTL |
|
||||
|---------|-----|-----|
|
||||
| Text alignment | Left | Right |
|
||||
| Navigation | Left side | Right side |
|
||||
| Progress indicators | Left to right | Right to left |
|
||||
| Icons with direction | → | ← |
|
||||
| Checkmarks | ✓ on right | ✓ on left |
|
||||
| Back buttons | ← | → |
|
||||
| Carousels/sliders | Swipe left for next | Swipe right for next |
|
||||
|
||||
### What NOT to Mirror
|
||||
|
||||
| Element | Reason |
|
||||
|---------|--------|
|
||||
| Numbers | Always LTR (123, not ٣٢١) |
|
||||
| Phone numbers | Universal format |
|
||||
| Clocks | Universal format |
|
||||
| Video player controls | Universal convention |
|
||||
| Brand logos | Design integrity |
|
||||
| Math equations | Universal notation |
|
||||
|
||||
### CSS for RTL
|
||||
|
||||
```css
|
||||
/* Modern approach using logical properties */
|
||||
.element {
|
||||
/* Instead of margin-left: 16px */
|
||||
margin-inline-start: 16px;
|
||||
|
||||
/* Instead of padding-right: 8px */
|
||||
padding-inline-end: 8px;
|
||||
|
||||
/* Instead of text-align: left */
|
||||
text-align: start;
|
||||
|
||||
/* Instead of float: left */
|
||||
float: inline-start;
|
||||
}
|
||||
|
||||
/* Or use dir attribute */
|
||||
[dir="rtl"] .element {
|
||||
/* RTL-specific overrides */
|
||||
}
|
||||
```
|
||||
|
||||
### RTL Icons
|
||||
|
||||
Icons with inherent direction need mirroring:
|
||||
|
||||
| Icon | Mirror? | Reason |
|
||||
|------|---------|--------|
|
||||
| Back arrow | Yes | Directional navigation |
|
||||
| Forward arrow | Yes | Directional navigation |
|
||||
| Reply icon | Yes | Shows direction of response |
|
||||
| Search icon | No | Magnifying glass is universal |
|
||||
| Home icon | No | House has no direction |
|
||||
| Settings gear | No | Symmetric |
|
||||
| Play button | No | Universal media convention |
|
||||
|
||||
---
|
||||
|
||||
## Color Meanings Across Cultures
|
||||
|
||||
### Red
|
||||
|
||||
| Culture | Meaning |
|
||||
|---------|---------|
|
||||
| Western | Danger, stop, error, love |
|
||||
| China | Good luck, prosperity, happiness |
|
||||
| India | Purity, fertility |
|
||||
| South Africa | Mourning |
|
||||
| Japan | Life, anger |
|
||||
|
||||
**Design implication:** Don't use red for error states in apps targeting Chinese markets without context.
|
||||
|
||||
### White
|
||||
|
||||
| Culture | Meaning |
|
||||
|---------|---------|
|
||||
| Western | Purity, cleanliness, peace |
|
||||
| China/Japan | Death, mourning |
|
||||
| India | Unhappiness, mourning |
|
||||
|
||||
**Design implication:** White space may have different connotations; "clean" design may feel empty or cold.
|
||||
|
||||
### Green
|
||||
|
||||
| Culture | Meaning |
|
||||
|---------|---------|
|
||||
| Western | Go, success, nature, money |
|
||||
| Islamic | Sacred, paradise |
|
||||
| China | Infidelity (green hat = cuckold) |
|
||||
| Ireland | National identity |
|
||||
|
||||
**Design implication:** Green success states work globally, but be careful with green accessories or hats in Chinese contexts.
|
||||
|
||||
### Yellow
|
||||
|
||||
| Culture | Meaning |
|
||||
|---------|---------|
|
||||
| Western | Caution, happiness |
|
||||
| Japan | Courage, royalty |
|
||||
| Egypt | Mourning |
|
||||
| Latin America | Death, mourning |
|
||||
|
||||
### Blue
|
||||
|
||||
| Culture | Meaning |
|
||||
|---------|---------|
|
||||
| Most cultures | Trust, calm, professionalism |
|
||||
| Iran | Mourning, spirituality |
|
||||
|
||||
**Design implication:** Blue is relatively safe globally; commonly used for links and primary actions.
|
||||
|
||||
### General Guidelines
|
||||
|
||||
1. **Test with local users** - Color perception varies
|
||||
2. **Don't rely on color alone** - Add icons and text
|
||||
3. **Provide customization** - Let users choose colors where possible
|
||||
4. **Research target markets** - Specific meanings in your target regions
|
||||
|
||||
---
|
||||
|
||||
## Form Conventions
|
||||
|
||||
### Name Fields
|
||||
|
||||
| Culture | Name Structure |
|
||||
|---------|----------------|
|
||||
| Western | First + Last (given + family) |
|
||||
| China/Japan/Korea | Family + Given |
|
||||
| Iceland | Given + Patronymic |
|
||||
| Spanish | Given + Father's surname + Mother's surname |
|
||||
| Arabic | Given + Father's + Grandfather's + Family |
|
||||
|
||||
**Best practices:**
|
||||
- Use single "Full Name" field when possible
|
||||
- If splitting, use "Given Name" and "Family Name" (not First/Last)
|
||||
- Don't assume first/last order
|
||||
- Allow long names (>50 characters)
|
||||
|
||||
### Address Fields
|
||||
|
||||
| Region | Considerations |
|
||||
|--------|----------------|
|
||||
| US | State, ZIP code (5 or 9 digits) |
|
||||
| UK | County optional, postcode format varies |
|
||||
| Japan | Prefecture, address reads large→small |
|
||||
| Brazil | CEP codes |
|
||||
| Countries without postal codes | 50+ countries don't use them |
|
||||
|
||||
**Best practices:**
|
||||
- Don't require postal code universally
|
||||
- Use country-appropriate field labels
|
||||
- Allow flexible formats
|
||||
- Consider address autocomplete services
|
||||
|
||||
### Phone Numbers
|
||||
|
||||
| Consideration | Approach |
|
||||
|---------------|----------|
|
||||
| Country codes | Allow input or select separately |
|
||||
| Length | Varies widely (7-15 digits) |
|
||||
| Format | Don't enforce specific format |
|
||||
| Mobile vs landline | Labels may not translate |
|
||||
|
||||
**Best practices:**
|
||||
- Accept multiple formats
|
||||
- Store in E.164 format internally (+1234567890)
|
||||
- Display in local format
|
||||
- Don't validate too strictly
|
||||
|
||||
### Dates
|
||||
|
||||
| Region | Format |
|
||||
|--------|--------|
|
||||
| US | MM/DD/YYYY |
|
||||
| Most of world | DD/MM/YYYY |
|
||||
| Japan, China, Korea | YYYY/MM/DD |
|
||||
| ISO standard | YYYY-MM-DD |
|
||||
|
||||
**Best practices:**
|
||||
- Use date pickers instead of text input
|
||||
- Show month names (not numbers) to avoid confusion
|
||||
- Store in ISO format (YYYY-MM-DD)
|
||||
- Display in user's locale
|
||||
|
||||
### Currency
|
||||
|
||||
| Consideration | Examples |
|
||||
|---------------|----------|
|
||||
| Symbol position | $100 vs 100€ vs 100 kr |
|
||||
| Decimal separator | $1,234.56 vs €1.234,56 |
|
||||
| Thousands separator | 1,000 vs 1.000 vs 1 000 |
|
||||
| Currency names | Dollar, Yuan, Rupee |
|
||||
|
||||
**Best practices:**
|
||||
- Format according to user locale
|
||||
- Always show currency symbol/code
|
||||
- Be clear about which currency
|
||||
- Handle conversion if multi-currency
|
||||
|
||||
---
|
||||
|
||||
## Text Considerations
|
||||
|
||||
### Text Expansion
|
||||
|
||||
Translated text is often longer than English:
|
||||
|
||||
| Language | Expansion Factor |
|
||||
|----------|------------------|
|
||||
| German | 1.3x |
|
||||
| French | 1.2x |
|
||||
| Russian | 1.2x |
|
||||
| Spanish | 1.2x |
|
||||
| Chinese | 0.8x |
|
||||
| Japanese | 0.9x |
|
||||
|
||||
**Design implications:**
|
||||
- Don't design to exact English text length
|
||||
- Allow buttons and labels to expand
|
||||
- Test layouts with longest expected translations
|
||||
- Use flexible grid/flexbox layouts
|
||||
|
||||
### Text in Images
|
||||
|
||||
Avoid text in images because:
|
||||
- Can't be translated easily
|
||||
- Not accessible to screen readers
|
||||
- Doesn't scale with user preferences
|
||||
- Complicates localization workflow
|
||||
|
||||
**Alternative:** Use CSS text over images.
|
||||
|
||||
### Numbers and Units
|
||||
|
||||
| Element | Consideration |
|
||||
|---------|---------------|
|
||||
| Measurement | Metric (most world) vs Imperial (US) |
|
||||
| Paper size | A4 (most world) vs Letter (US, Canada) |
|
||||
| Temperature | Celsius vs Fahrenheit |
|
||||
| Time | 24-hour vs 12-hour (AM/PM) |
|
||||
|
||||
---
|
||||
|
||||
## Icons and Imagery
|
||||
|
||||
### Potentially Problematic Icons
|
||||
|
||||
| Icon | Issue | Alternative |
|
||||
|------|-------|-------------|
|
||||
| Mailbox | US-specific design | Envelope |
|
||||
| Check mark | Means "wrong" in some cultures | Consider context |
|
||||
| Thumbs up | Offensive in some cultures | Hearts, stars |
|
||||
| Hand gestures | Vary widely in meaning | Avoid gestures |
|
||||
| Animals | Religious/cultural sensitivities | Research target market |
|
||||
| Religious symbols | May exclude or offend | Use neutral symbols |
|
||||
|
||||
### Photography
|
||||
|
||||
| Consideration | Approach |
|
||||
|---------------|----------|
|
||||
| Diversity | Represent target audience |
|
||||
| Gestures | Avoid culture-specific gestures |
|
||||
| Clothing | Consider cultural norms |
|
||||
| Settings | Use locally relevant contexts |
|
||||
| Food | Be aware of dietary restrictions |
|
||||
|
||||
---
|
||||
|
||||
## Localization Best Practices
|
||||
|
||||
### Technical
|
||||
|
||||
```javascript
|
||||
// Use internationalization libraries
|
||||
const formatter = new Intl.DateTimeFormat('de-DE');
|
||||
const date = formatter.format(new Date());
|
||||
|
||||
const currencyFormatter = new Intl.NumberFormat('ja-JP', {
|
||||
style: 'currency',
|
||||
currency: 'JPY'
|
||||
});
|
||||
const price = currencyFormatter.format(1000);
|
||||
```
|
||||
|
||||
### Content
|
||||
|
||||
- Use simple, clear language (easier to translate)
|
||||
- Avoid idioms and colloquialisms
|
||||
- Don't hardcode strings (use translation keys)
|
||||
- Provide context for translators
|
||||
- Test with pseudo-localization during development
|
||||
|
||||
### Design
|
||||
|
||||
- Design for flexibility (expanding text)
|
||||
- Use icons with text (not alone)
|
||||
- Test layouts in RTL and longest languages
|
||||
- Consider reading order in complex layouts
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference Checklist
|
||||
|
||||
Before launching internationally:
|
||||
|
||||
**Layout:**
|
||||
- [ ] Supports RTL if targeting Arabic, Hebrew, Persian
|
||||
- [ ] Text can expand 30% without breaking
|
||||
- [ ] No text in images
|
||||
- [ ] Icons are culturally neutral
|
||||
|
||||
**Forms:**
|
||||
- [ ] Name fields are flexible
|
||||
- [ ] Addresses work without postal codes
|
||||
- [ ] Phone numbers accept various formats
|
||||
- [ ] Dates use pickers or clear formats
|
||||
|
||||
**Content:**
|
||||
- [ ] Colors don't carry unintended meaning
|
||||
- [ ] Images represent target audience
|
||||
- [ ] No culture-specific idioms
|
||||
- [ ] Units match target region
|
||||
|
||||
**Technical:**
|
||||
- [ ] Dates, numbers, currency use locale formatting
|
||||
- [ ] All strings externalized for translation
|
||||
- [ ] Character encoding supports target languages
|
||||
- [ ] Font supports required character sets
|
||||
@@ -0,0 +1,362 @@
|
||||
# Dark Patterns in UX Design
|
||||
|
||||
Understanding manipulative design practices to recognize and avoid them. Ethical alternatives that achieve business goals without deceiving users.
|
||||
|
||||
## What Are Dark Patterns?
|
||||
|
||||
Dark patterns are user interface designs that trick users into doing things they didn't intend. They exploit cognitive biases and psychological vulnerabilities for business benefit at user expense.
|
||||
|
||||
**Key distinction:**
|
||||
- Persuasion = helping users make decisions aligned with their goals
|
||||
- Dark patterns = tricking users into decisions against their interests
|
||||
|
||||
---
|
||||
|
||||
## Categories of Dark Patterns
|
||||
|
||||
### 1. Forced Continuity
|
||||
|
||||
**Definition:** Making it easy to sign up for a free trial but difficult to cancel.
|
||||
|
||||
**Examples:**
|
||||
- Require phone call to cancel (but signup was online)
|
||||
- Bury cancellation in settings maze
|
||||
- Show "Are you sure?" modals repeatedly
|
||||
- Require cancellation 30 days before renewal
|
||||
|
||||
**Why it's harmful:**
|
||||
- Users pay for services they don't want
|
||||
- Exploits inertia and forgetfulness
|
||||
- Damages trust when discovered
|
||||
|
||||
**Ethical alternative:**
|
||||
- Cancel button as easy to find as signup
|
||||
- Clear cancellation confirmation (not guilt trips)
|
||||
- Email reminder before renewal charge
|
||||
- Allow pause instead of forcing cancel
|
||||
|
||||
### 2. Roach Motel
|
||||
|
||||
**Definition:** Easy to get into a situation, hard to get out.
|
||||
|
||||
**Examples:**
|
||||
- Account creation takes 1 minute, deletion takes 30 days and support tickets
|
||||
- Subscribing to emails requires one click, unsubscribing requires login + multiple confirmations
|
||||
- Joining is free, but exported data costs money
|
||||
|
||||
**Why it's harmful:**
|
||||
- Traps users against their will
|
||||
- Violates user autonomy
|
||||
- Often illegal under GDPR and similar regulations
|
||||
|
||||
**Ethical alternative:**
|
||||
- Symmetric design: if X is easy, reversing X should be easy
|
||||
- Account deletion should be self-service
|
||||
- Data export should be free and complete
|
||||
- Unsubscribe = one click
|
||||
|
||||
### 3. Privacy Zuckering
|
||||
|
||||
**Definition:** Tricking users into sharing more information than intended.
|
||||
|
||||
**Examples:**
|
||||
- Default settings share everything publicly
|
||||
- "Connect with friends" imports entire contact list
|
||||
- Profile completion gamification encourages oversharing
|
||||
- Confusing privacy controls that require expertise
|
||||
|
||||
**Why it's harmful:**
|
||||
- Users lose control of personal information
|
||||
- Can lead to real-world harm (stalking, discrimination)
|
||||
- Exploits the complexity of privacy settings
|
||||
|
||||
**Ethical alternative:**
|
||||
- Privacy-respecting defaults (share nothing by default)
|
||||
- Clear, plain-language privacy explanations
|
||||
- Granular, understandable controls
|
||||
- Regular privacy checkups that surface settings
|
||||
|
||||
### 4. Bait and Switch
|
||||
|
||||
**Definition:** User sets out to do one thing, but something different happens.
|
||||
|
||||
**Examples:**
|
||||
- "X" button that triggers action instead of closing
|
||||
- "Download" button that's actually an ad
|
||||
- "Free trial" that immediately charges
|
||||
- Changing terms after user commits to purchase
|
||||
|
||||
**Why it's harmful:**
|
||||
- Directly deceives users about consequences
|
||||
- Violates fundamental expectations
|
||||
- Often results in unwanted charges or actions
|
||||
|
||||
**Ethical alternative:**
|
||||
- Buttons do exactly what they say
|
||||
- Clear labeling distinguishes ads from content
|
||||
- Free trials are genuinely free until stated conversion point
|
||||
- Terms are locked at time of agreement
|
||||
|
||||
### 5. Confirmshaming
|
||||
|
||||
**Definition:** Using guilt or shame to manipulate users into opting in.
|
||||
|
||||
**Examples:**
|
||||
- "No thanks, I don't want to save money"
|
||||
- "I'll stay ignorant" (for newsletter)
|
||||
- "I don't care about my health"
|
||||
- Imagery showing sad faces for decline option
|
||||
|
||||
**Why it's harmful:**
|
||||
- Manipulates emotions to override rational decision
|
||||
- Disrespects user autonomy
|
||||
- Creates negative brand association
|
||||
|
||||
**Ethical alternative:**
|
||||
- Neutral decline options: "No thanks" or "Maybe later"
|
||||
- Equal visual weight for both choices
|
||||
- Respect the "no" without comment
|
||||
- Focus on value proposition, not guilt
|
||||
|
||||
### 6. Hidden Costs
|
||||
|
||||
**Definition:** Prices or fees revealed only at final checkout.
|
||||
|
||||
**Examples:**
|
||||
- Service fees added at last step
|
||||
- Required "convenience fees"
|
||||
- Shipping costs revealed after entering payment info
|
||||
- "Processing fees" on top of advertised price
|
||||
|
||||
**Why it's harmful:**
|
||||
- Users commit time/effort before learning true cost
|
||||
- Exploits sunk cost fallacy
|
||||
- Illegal in many jurisdictions (price must be clear)
|
||||
|
||||
**Ethical alternative:**
|
||||
- Show total cost including all fees upfront
|
||||
- If fees depend on choices, show estimates early
|
||||
- Price transparency builds trust
|
||||
- All-in pricing where possible
|
||||
|
||||
### 7. Misdirection
|
||||
|
||||
**Definition:** Design draws attention away from important information.
|
||||
|
||||
**Examples:**
|
||||
- Terms and conditions in tiny gray text
|
||||
- "Yes" button prominent, "No" button hidden
|
||||
- Pre-selected add-ons that require unchecking
|
||||
- Important disclaimers below the fold
|
||||
|
||||
**Why it's harmful:**
|
||||
- Prevents informed decision-making
|
||||
- Hides information users would want to know
|
||||
- Exploits visual hierarchy against users
|
||||
|
||||
**Ethical alternative:**
|
||||
- Important information is visually prominent
|
||||
- Both options equally accessible
|
||||
- Nothing pre-selected that costs money
|
||||
- Disclaimers at point of relevance, not hidden
|
||||
|
||||
### 8. Trick Questions
|
||||
|
||||
**Definition:** Confusing wording that leads to unintended choices.
|
||||
|
||||
**Examples:**
|
||||
- "Uncheck to not receive no emails" (double negative)
|
||||
- Checkboxes that mean opposite things mixed together
|
||||
- "Continue" meaning "I agree" without stating so
|
||||
- Questions worded to confuse opt-in vs opt-out
|
||||
|
||||
**Why it's harmful:**
|
||||
- Deliberately confuses users
|
||||
- Results in choices user didn't mean to make
|
||||
- Exploits cognitive load
|
||||
|
||||
**Ethical alternative:**
|
||||
- Clear, simple language
|
||||
- Consistent meaning (check = yes, uncheck = no)
|
||||
- Explicit confirmation language
|
||||
- User testing to catch confusing wording
|
||||
|
||||
### 9. Sneak into Basket
|
||||
|
||||
**Definition:** Items added to cart without user action.
|
||||
|
||||
**Examples:**
|
||||
- Insurance pre-selected during checkout
|
||||
- "Protection plan" added by default
|
||||
- Donation to charity checked by default
|
||||
- Accessories added when buying main product
|
||||
|
||||
**Why it's harmful:**
|
||||
- Users pay for things they didn't choose
|
||||
- Exploits inattention during checkout
|
||||
- Often hidden in long checkout flows
|
||||
|
||||
**Ethical alternative:**
|
||||
- Nothing added without explicit user action
|
||||
- Optional items clearly offered (not pre-selected)
|
||||
- Cart contents always visible and editable
|
||||
- Confirmation of what's being purchased
|
||||
|
||||
### 10. Urgency & Scarcity (False)
|
||||
|
||||
**Definition:** Creating fake urgency or scarcity to pressure decisions.
|
||||
|
||||
**Examples:**
|
||||
- "Only 2 left!" (restocked hourly)
|
||||
- "This deal expires in 10:00" (resets on refresh)
|
||||
- "15 people viewing this" (fabricated)
|
||||
- "Prices increase tomorrow" (they don't)
|
||||
|
||||
**Why it's harmful:**
|
||||
- Pressures users into hasty decisions
|
||||
- Based on lies (not real scarcity)
|
||||
- Prevents price comparison and consideration
|
||||
- Particularly harmful for high-stakes purchases
|
||||
|
||||
**Ethical alternative:**
|
||||
- Only show real inventory counts
|
||||
- Honest sale end dates
|
||||
- If scarcity is real, explain why
|
||||
- Give users time to decide
|
||||
|
||||
---
|
||||
|
||||
## Regulatory Context
|
||||
|
||||
### GDPR (Europe)
|
||||
|
||||
Dark patterns affecting consent are illegal:
|
||||
- Consent must be freely given
|
||||
- Rejecting must be as easy as accepting
|
||||
- Pre-ticked boxes invalid for consent
|
||||
- Bundled consent (all-or-nothing) invalid
|
||||
|
||||
### FTC (United States)
|
||||
|
||||
The FTC has taken action against:
|
||||
- Hidden subscription fees
|
||||
- Difficult cancellation processes
|
||||
- Misleading "free trial" offers
|
||||
- Fake urgency and scarcity
|
||||
|
||||
### California Privacy Rights Act (CPRA)
|
||||
|
||||
Specifically prohibits:
|
||||
- Dark patterns in opt-out processes
|
||||
- Requires symmetry in design
|
||||
- Consent obtained through dark patterns is invalid
|
||||
|
||||
---
|
||||
|
||||
## How to Audit for Dark Patterns
|
||||
|
||||
### Checklist
|
||||
|
||||
**Signup/Subscription:**
|
||||
- [ ] Can users cancel as easily as they signed up?
|
||||
- [ ] Are renewal terms clear at signup?
|
||||
- [ ] Is the "free" trial genuinely free?
|
||||
|
||||
**Checkout:**
|
||||
- [ ] Is the total price clear before final step?
|
||||
- [ ] Are all added items explicitly chosen by user?
|
||||
- [ ] Are opt-outs as prominent as opt-ins?
|
||||
|
||||
**Data/Privacy:**
|
||||
- [ ] Are privacy settings understandable?
|
||||
- [ ] Are defaults privacy-respecting?
|
||||
- [ ] Can users export/delete their data easily?
|
||||
|
||||
**General:**
|
||||
- [ ] Does every button do what it says?
|
||||
- [ ] Is important information visually prominent?
|
||||
- [ ] Are decline options neutral (no shaming)?
|
||||
- [ ] Is urgency/scarcity real?
|
||||
|
||||
### The Mirror Test
|
||||
|
||||
Ask: "Would I feel comfortable if a journalist wrote about how this works?"
|
||||
|
||||
If the answer is no, it's probably a dark pattern.
|
||||
|
||||
---
|
||||
|
||||
## Ethical Alternatives That Work
|
||||
|
||||
### Instead of Forced Continuity
|
||||
|
||||
**Business goal:** Retain subscribers
|
||||
|
||||
**Ethical approach:**
|
||||
- Make the product so good they don't want to cancel
|
||||
- Offer pause option instead of cancel
|
||||
- Win-back campaigns for churned users
|
||||
- Ask why they're leaving and address it
|
||||
|
||||
### Instead of Hidden Costs
|
||||
|
||||
**Business goal:** Competitive-looking prices
|
||||
|
||||
**Ethical approach:**
|
||||
- All-in pricing (include fees in advertised price)
|
||||
- Compete on value, not deceptive pricing
|
||||
- Explain what fees cover (transparency builds trust)
|
||||
- Offer fee-free options (digital delivery, etc.)
|
||||
|
||||
### Instead of Confirmshaming
|
||||
|
||||
**Business goal:** Higher opt-in rates
|
||||
|
||||
**Ethical approach:**
|
||||
- Stronger value proposition
|
||||
- Social proof (join 100k subscribers)
|
||||
- Clear benefit statement
|
||||
- Respect "no" and try again later
|
||||
|
||||
### Instead of False Urgency
|
||||
|
||||
**Business goal:** Faster purchase decisions
|
||||
|
||||
**Ethical approach:**
|
||||
- Genuine limited-time offers (and honor them)
|
||||
- Waitlists for genuinely scarce items
|
||||
- Early access for committed customers
|
||||
- Value-based urgency (limited capacity, real deadlines)
|
||||
|
||||
---
|
||||
|
||||
## The Business Case Against Dark Patterns
|
||||
|
||||
### Short-term vs Long-term
|
||||
|
||||
| Metric | Dark Pattern Impact | Ethical Design Impact |
|
||||
|--------|---------------------|----------------------|
|
||||
| Initial conversion | ↑ Higher | Slightly lower |
|
||||
| Customer trust | ↓ Lower | ↑ Higher |
|
||||
| Churn rate | ↑ Higher | ↓ Lower |
|
||||
| Customer lifetime value | ↓ Lower | ↑ Higher |
|
||||
| Word of mouth | Negative | Positive |
|
||||
| Regulatory risk | High | Low |
|
||||
|
||||
### Real Costs of Dark Patterns
|
||||
|
||||
1. **Support costs** - Dealing with angry customers
|
||||
2. **Chargeback rates** - Users disputing unwanted charges
|
||||
3. **Reputation damage** - Social media exposure
|
||||
4. **Legal fees** - Defending against lawsuits
|
||||
5. **Regulatory fines** - Increasing enforcement
|
||||
6. **Employee morale** - Good people don't want to deceive users
|
||||
|
||||
### Companies That Changed
|
||||
|
||||
Several major companies have eliminated dark patterns after backlash:
|
||||
- LinkedIn simplified privacy controls after criticism
|
||||
- Amazon made "Subscribe & Save" more transparent
|
||||
- Apple added App Store subscription management
|
||||
|
||||
The pattern: Short-term thinking creates dark patterns; long-term thinking removes them.
|
||||
@@ -0,0 +1,296 @@
|
||||
# Resolving Heuristic Conflicts
|
||||
|
||||
When usability principles contradict each other—frameworks for making trade-off decisions.
|
||||
|
||||
## The Nature of Conflicts
|
||||
|
||||
Usability heuristics are guidelines, not laws. In real design, they often pull in opposite directions. Good UX design requires recognizing these tensions and making thoughtful trade-offs.
|
||||
|
||||
**Common tension patterns:**
|
||||
- Simplicity vs. Power
|
||||
- Consistency vs. Optimal for context
|
||||
- Efficiency vs. Error prevention
|
||||
- User control vs. Guidance
|
||||
- Discoverability vs. Clean interface
|
||||
|
||||
---
|
||||
|
||||
## Major Heuristic Conflicts
|
||||
|
||||
### 1. Simplicity vs. Flexibility
|
||||
|
||||
**The tension:**
|
||||
- "Keep it simple" (Krug, Nielsen)
|
||||
- "Support user control and freedom" (Nielsen #3)
|
||||
- "Flexibility and efficiency of use" (Nielsen #7)
|
||||
|
||||
**Example:** Photo editing app
|
||||
- Simple: Few options, quick editing
|
||||
- Flexible: Many controls, professional results
|
||||
|
||||
**Resolution framework:**
|
||||
|
||||
| Factor | Lean Simple | Lean Flexible |
|
||||
|--------|-------------|---------------|
|
||||
| User expertise | Novice | Expert |
|
||||
| Task frequency | Occasional | Daily |
|
||||
| Consequences of error | Low | High |
|
||||
| Time pressure | High | Low |
|
||||
|
||||
**Design patterns:**
|
||||
- Progressive disclosure (simple default, power underneath)
|
||||
- Personas-based modes (Basic/Advanced)
|
||||
- Contextual features (show when relevant)
|
||||
|
||||
### 2. Consistency vs. Context Optimization
|
||||
|
||||
**The tension:**
|
||||
- "Consistency and standards" (Nielsen #4)
|
||||
- "Match between system and real world" (Nielsen #2)
|
||||
|
||||
**Example:** Destructive actions
|
||||
- Consistent: Red "Delete" button everywhere
|
||||
- Context-optimized: Prominent delete for items that should be deleted, hidden for critical data
|
||||
|
||||
**Resolution framework:**
|
||||
|
||||
| Factor | Lean Consistent | Lean Contextual |
|
||||
|--------|-----------------|-----------------|
|
||||
| User base diversity | High | Low |
|
||||
| Learning curve concern | Yes | No |
|
||||
| Action frequency | Common | Rare |
|
||||
| Mental model strength | Established | Forming |
|
||||
|
||||
**Design patterns:**
|
||||
- Consistent patterns, contextual prominence
|
||||
- Same actions, different emphasis
|
||||
- Gradual introduction of contextual variations
|
||||
|
||||
### 3. Efficiency vs. Error Prevention
|
||||
|
||||
**The tension:**
|
||||
- "Error prevention" (Nielsen #5)
|
||||
- "Flexibility and efficiency of use" (Nielsen #7)
|
||||
|
||||
**Example:** Checkout flow
|
||||
- Error prevention: Confirmation at every step
|
||||
- Efficiency: One-click purchase
|
||||
|
||||
**Resolution framework:**
|
||||
|
||||
| Factor | Lean Error Prevention | Lean Efficiency |
|
||||
|--------|----------------------|-----------------|
|
||||
| Reversibility | Irreversible | Easily undone |
|
||||
| Cost of error | High (money, data) | Low |
|
||||
| User familiarity | New users | Power users |
|
||||
| Frequency | Rare | Very frequent |
|
||||
|
||||
**Design patterns:**
|
||||
- Undo instead of confirm (efficient + safe)
|
||||
- Confidence-based friction (add steps for risky actions)
|
||||
- User-controlled safety level
|
||||
|
||||
### 4. Discoverability vs. Clean Interface
|
||||
|
||||
**The tension:**
|
||||
- "Recognition rather than recall" (Nielsen #6)
|
||||
- "Aesthetic and minimalist design" (Nielsen #8)
|
||||
|
||||
**Example:** Feature-rich application
|
||||
- Discoverable: Show all options visibly
|
||||
- Clean: Hide features until needed
|
||||
|
||||
**Resolution framework:**
|
||||
|
||||
| Factor | Lean Discoverable | Lean Clean |
|
||||
|--------|-------------------|------------|
|
||||
| Feature frequency | Core features | Edge cases |
|
||||
| User expertise | Beginner | Expert |
|
||||
| Task complexity | Simple | Complex |
|
||||
| Screen real estate | Generous | Limited |
|
||||
|
||||
**Design patterns:**
|
||||
- Primary actions visible, secondary in menus
|
||||
- Progressive disclosure
|
||||
- Contextual menus
|
||||
- Search/command palettes for power users
|
||||
|
||||
### 5. Guidance vs. User Control
|
||||
|
||||
**The tension:**
|
||||
- Help users avoid errors
|
||||
- Respect user autonomy
|
||||
|
||||
**Example:** Form validation
|
||||
- Guidance: Prevent submission until valid
|
||||
- Control: Let users submit and see what happens
|
||||
|
||||
**Resolution framework:**
|
||||
|
||||
| Factor | Lean Guidance | Lean Control |
|
||||
|--------|---------------|--------------|
|
||||
| User expertise | Novice | Expert |
|
||||
| Error recovery | Difficult | Easy |
|
||||
| System tolerance | Low (strict rules) | High (flexible) |
|
||||
| User frustration with restrictions | Low | High |
|
||||
|
||||
**Design patterns:**
|
||||
- Inline guidance (not blocking)
|
||||
- Warnings vs. blockers
|
||||
- "Are you sure?" rather than "You can't"
|
||||
|
||||
---
|
||||
|
||||
## Resolution Framework
|
||||
|
||||
### Step 1: Identify the Conflict
|
||||
|
||||
Name the specific heuristics in tension:
|
||||
- Which principle says do A?
|
||||
- Which principle says do B?
|
||||
- Why can't we fully satisfy both?
|
||||
|
||||
### Step 2: Assess the Context
|
||||
|
||||
Consider:
|
||||
- **User type:** Novice vs. expert
|
||||
- **Task criticality:** Browsing vs. financial transaction
|
||||
- **Frequency:** One-time vs. daily
|
||||
- **Reversibility:** Can they undo?
|
||||
- **Consequence:** What happens if wrong?
|
||||
|
||||
### Step 3: Prioritize for This Context
|
||||
|
||||
| Context | Usually Prioritize |
|
||||
|---------|-------------------|
|
||||
| Critical data, irreversible | Error prevention > Efficiency |
|
||||
| Frequent actions, low stakes | Efficiency > Error prevention |
|
||||
| New users, unfamiliar domain | Guidance > Control |
|
||||
| Expert users, familiar domain | Flexibility > Simplicity |
|
||||
| Limited screen, focused task | Minimalism > Discoverability |
|
||||
| Learning interface | Discoverability > Minimalism |
|
||||
|
||||
### Step 4: Design for Both When Possible
|
||||
|
||||
Often, clever design satisfies both:
|
||||
|
||||
| Conflict | Both-And Solution |
|
||||
|----------|-------------------|
|
||||
| Simple vs. Powerful | Progressive disclosure |
|
||||
| Efficient vs. Safe | Undo instead of confirm |
|
||||
| Clean vs. Discoverable | Contextual reveal |
|
||||
| Consistent vs. Optimal | Consistent patterns, variable emphasis |
|
||||
| Guided vs. Controlled | Warnings, not blockers |
|
||||
|
||||
### Step 5: Test the Trade-off
|
||||
|
||||
Validate with real users:
|
||||
- Does the chosen priority work for target users?
|
||||
- Are edge cases handled acceptably?
|
||||
- Do users understand why limitations exist?
|
||||
|
||||
---
|
||||
|
||||
## Common Conflict Scenarios
|
||||
|
||||
### Scenario: Onboarding Flow
|
||||
|
||||
**Conflict:** Efficiency (skip it) vs. Guidance (require it)
|
||||
|
||||
**Resolution:**
|
||||
- Allow skip but show value
|
||||
- Defer to contextual moments
|
||||
- Progressive onboarding during natural use
|
||||
|
||||
### Scenario: Mobile Navigation
|
||||
|
||||
**Conflict:** Discoverability (show all options) vs. Minimalism (hamburger menu)
|
||||
|
||||
**Resolution:**
|
||||
- Bottom navigation for 3-5 key items
|
||||
- Hamburger for secondary items
|
||||
- Tab bar > hamburger for primary navigation
|
||||
|
||||
### Scenario: Form Validation
|
||||
|
||||
**Conflict:** Error prevention (validate immediately) vs. Efficiency (let them type)
|
||||
|
||||
**Resolution:**
|
||||
- Validate on blur (not on keystroke)
|
||||
- Show errors inline, not modal
|
||||
- Allow submission, show all errors
|
||||
|
||||
### Scenario: Confirmation Dialogs
|
||||
|
||||
**Conflict:** Error prevention (confirm everything) vs. Efficiency (just do it)
|
||||
|
||||
**Resolution:**
|
||||
- Confirm only irreversible/high-cost actions
|
||||
- Provide undo instead of confirm
|
||||
- Use clear language about consequences
|
||||
|
||||
### Scenario: Default Settings
|
||||
|
||||
**Conflict:** User control (let them configure) vs. Simplicity (sensible defaults)
|
||||
|
||||
**Resolution:**
|
||||
- Smart defaults that work for 80%
|
||||
- Easy access to change settings
|
||||
- Don't require configuration to start
|
||||
|
||||
---
|
||||
|
||||
## Decision Documentation
|
||||
|
||||
When making trade-off decisions, document:
|
||||
|
||||
```markdown
|
||||
## Design Decision: [Feature]
|
||||
|
||||
### Conflict
|
||||
[Heuristic A] suggests we should...
|
||||
[Heuristic B] suggests we should...
|
||||
|
||||
### Context
|
||||
- User type: [Novice/Expert]
|
||||
- Task: [Critical/Casual]
|
||||
- Frequency: [Daily/Occasional]
|
||||
- Reversibility: [Yes/No]
|
||||
|
||||
### Decision
|
||||
We chose to prioritize [Heuristic] because...
|
||||
|
||||
### Mitigation
|
||||
We addressed the other concern by...
|
||||
|
||||
### Validation
|
||||
We'll know this is right when...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Principles
|
||||
|
||||
### 1. Context Is King
|
||||
|
||||
The same conflict should be resolved differently in different contexts. A banking app and a social media app may reach opposite conclusions.
|
||||
|
||||
### 2. Know Your Users
|
||||
|
||||
The trade-off depends heavily on user expertise and expectations. Test with real users, not assumptions.
|
||||
|
||||
### 3. Design for Both When Possible
|
||||
|
||||
Often, the conflict is artificial and clever design can satisfy both principles.
|
||||
|
||||
### 4. Make Conscious Trade-offs
|
||||
|
||||
Don't accidentally violate a heuristic. Violate it consciously with reasoning.
|
||||
|
||||
### 5. Document Decisions
|
||||
|
||||
Future you (and your team) will want to know why choices were made.
|
||||
|
||||
### 6. Revisit Trade-offs
|
||||
|
||||
As products and users evolve, optimal trade-offs may shift. Periodically review past decisions.
|
||||
@@ -0,0 +1,289 @@
|
||||
# Krug's Usability Principles
|
||||
|
||||
Full methodology from "Don't Make Me Think" for creating intuitive, usable interfaces.
|
||||
|
||||
## The Reality of How Users Use the Web
|
||||
|
||||
### Fact 1: We Don't Read, We Scan
|
||||
|
||||
**Why users scan:**
|
||||
- They're on a mission (have a goal)
|
||||
- They know they don't need to read everything
|
||||
- They're good at scanning from years of practice
|
||||
|
||||
**Design implications:**
|
||||
- Create visual hierarchy (important things bigger, bolder)
|
||||
- Use headings and subheadings liberally
|
||||
- Keep paragraphs short
|
||||
- Use bulleted lists
|
||||
- Highlight key terms
|
||||
|
||||
### Fact 2: We Don't Make Optimal Choices, We Satisfice
|
||||
|
||||
**Satisficing** = "satisfying" + "sufficing"
|
||||
|
||||
Users click the first reasonable option, not the best option.
|
||||
|
||||
**Why satisficing:**
|
||||
- Optimizing is hard and takes time
|
||||
- The penalty for guessing wrong is low (back button)
|
||||
- Weighing options doesn't improve results much
|
||||
- Guessing is more fun
|
||||
|
||||
**Design implications:**
|
||||
- Make the right choice obvious
|
||||
- Don't rely on users reading all options
|
||||
- Make consequences clear before clicking
|
||||
|
||||
### Fact 3: We Don't Figure Out How Things Work, We Muddle Through
|
||||
|
||||
Users don't read instructions. They try things until something works.
|
||||
|
||||
**Why muddling through:**
|
||||
- It's not important to them to know how it works
|
||||
- If they find something that works, they stick with it
|
||||
- It rarely matters if they don't use something optimally
|
||||
|
||||
**Design implications:**
|
||||
- Make things obvious so "figuring out" isn't needed
|
||||
- Expect users to use things "wrong"
|
||||
- Design for recovery from mistakes
|
||||
|
||||
---
|
||||
|
||||
## Things You Should Never Do
|
||||
|
||||
### Don't Make Users Think
|
||||
|
||||
Signs that your page makes users think:
|
||||
|
||||
| Symptom | Example |
|
||||
|---------|---------|
|
||||
| Puzzling labels | "Solutions" (solutions to what?) |
|
||||
| Links that could go anywhere | "Click here" |
|
||||
| Unexplained options | Checkboxes without context |
|
||||
| Unfamiliar terminology | Industry jargon |
|
||||
| Gratuitous cleverness | Puns, wordplay in navigation |
|
||||
|
||||
### Don't Waste Users' Goodwill
|
||||
|
||||
Users have a finite reservoir of goodwill:
|
||||
|
||||
**Things that diminish goodwill:**
|
||||
- Hiding info they need (phone numbers, prices)
|
||||
- Punishing them for not doing things your way
|
||||
- Asking for unnecessary information
|
||||
- Making them feel stupid
|
||||
- Making them repeat themselves
|
||||
- Sites that look like an afterthought
|
||||
- Amateur errors (broken links, typos)
|
||||
|
||||
**Things that increase goodwill:**
|
||||
- Know what questions they have and answer them
|
||||
- Minimize steps
|
||||
- Put effort into UI quality
|
||||
- Know what they're likely to struggle with
|
||||
- Make it easy to recover from errors
|
||||
- Apologize when things go wrong
|
||||
|
||||
### Don't Make Words Seem Important If They're Not
|
||||
|
||||
**Happy talk example:**
|
||||
> "Welcome to our website! We're excited to help you find exactly what you're looking for. Our team of dedicated professionals is committed to providing you with the best possible experience."
|
||||
|
||||
**Reality:** Users skip this. It says nothing.
|
||||
|
||||
**Rule:** If users will skip it, remove it.
|
||||
|
||||
---
|
||||
|
||||
## Navigation Must-Haves
|
||||
|
||||
### The Permanent Navigation
|
||||
|
||||
Every page needs:
|
||||
|
||||
1. **Site ID** (logo/name) - Top left corner
|
||||
2. **Page name** - Prominent, matches link that brought them
|
||||
3. **Sections** - Major site areas
|
||||
4. **Local navigation** - What's in this section
|
||||
5. **Utilities** - Sign in, Search, Help, Cart
|
||||
6. **"You are here" indicator** - Highlighted nav item
|
||||
|
||||
### The Trunk Test
|
||||
|
||||
Can users answer these on any random page?
|
||||
|
||||
| Question | Element That Answers It |
|
||||
|----------|------------------------|
|
||||
| What site is this? | Logo/Site ID |
|
||||
| What page am I on? | Page title |
|
||||
| What major sections exist? | Main navigation |
|
||||
| What are my options here? | Local navigation |
|
||||
| Where am I in the structure? | Breadcrumbs, highlighted nav |
|
||||
| How can I search? | Search box |
|
||||
|
||||
### Breadcrumbs
|
||||
|
||||
**Good breadcrumbs:**
|
||||
```
|
||||
Home > Products > Shoes > Running > Men's Trail Runners
|
||||
```
|
||||
|
||||
**Rules:**
|
||||
- Put them at the top
|
||||
- Use ">" between levels
|
||||
- Make the current page name visible but not a link
|
||||
- Use small text (secondary importance)
|
||||
|
||||
---
|
||||
|
||||
## Homepage Guidelines
|
||||
|
||||
The homepage has to do too many things:
|
||||
|
||||
1. Site identity and mission
|
||||
2. Site hierarchy (navigation)
|
||||
3. Search
|
||||
4. Teases (content, features)
|
||||
5. Timely content
|
||||
6. Deals
|
||||
7. Shortcuts (popular items)
|
||||
8. Registration/Sign in
|
||||
|
||||
### Homepage Priorities
|
||||
|
||||
**Must do:**
|
||||
- Tell me what this site is
|
||||
- Tell me what I can do here
|
||||
- Tell me why I should be here (and not somewhere else)
|
||||
- Start me on my way
|
||||
|
||||
**Should do:**
|
||||
- Show me what I'm looking for
|
||||
- Show me where to start
|
||||
- Establish credibility and trust
|
||||
|
||||
### The Big Bang Theory of Web Design
|
||||
|
||||
**You have 3-4 seconds to answer:**
|
||||
1. What is this?
|
||||
2. What can I do here?
|
||||
3. Why should I be here?
|
||||
|
||||
### Tagline Guidelines
|
||||
|
||||
| Good Tagline | Bad Tagline |
|
||||
|--------------|-------------|
|
||||
| Conveys unique value | Generic platitude |
|
||||
| Specific and informative | Vague and fluffy |
|
||||
| 6-8 words | Too long or too short |
|
||||
| Instantly understandable | Requires thought |
|
||||
|
||||
Examples:
|
||||
- Good: "Find anything from thousands of stores"
|
||||
- Bad: "Welcome to the future of shopping"
|
||||
|
||||
---
|
||||
|
||||
## Mobile Usability
|
||||
|
||||
### Mobile Considerations
|
||||
|
||||
**Constraints:**
|
||||
- Smaller viewport
|
||||
- Fat fingers (need bigger targets)
|
||||
- Single-column layout
|
||||
- No hover states
|
||||
- Variable attention and context
|
||||
|
||||
### Mobile Specifics
|
||||
|
||||
| Issue | Solution |
|
||||
|-------|----------|
|
||||
| Tiny tap targets | Minimum 44×44 px |
|
||||
| Crowded nav | Hamburger or bottom nav |
|
||||
| Long forms | Break into steps |
|
||||
| Hover-dependent UI | Alternative for touch |
|
||||
| Text too small | 16px minimum body text |
|
||||
|
||||
### Mobile Trade-offs
|
||||
|
||||
**What to prioritize:**
|
||||
- Primary tasks front and center
|
||||
- Essential content visible
|
||||
- Fast load times
|
||||
- Easy to tap, hard to mis-tap
|
||||
|
||||
**What to hide/remove:**
|
||||
- Secondary navigation
|
||||
- Non-essential images
|
||||
- Decorative elements
|
||||
- Long-form content
|
||||
|
||||
---
|
||||
|
||||
## Usability Testing on $0
|
||||
|
||||
### How Many Users?
|
||||
|
||||
**3-4 users catches most issues.**
|
||||
|
||||
Testing with more users has diminishing returns. Better to test with 3, fix issues, then test again with 3.
|
||||
|
||||
### What to Test
|
||||
|
||||
1. Can they complete core tasks?
|
||||
2. Where do they get stuck?
|
||||
3. What do they say out loud?
|
||||
4. What did they expect vs. what happened?
|
||||
|
||||
### Test Protocol
|
||||
|
||||
**Before:**
|
||||
- "I'm testing the site, not you"
|
||||
- "Think out loud as you go"
|
||||
- "There are no wrong answers"
|
||||
|
||||
**During:**
|
||||
- Don't help. Don't explain. Just watch.
|
||||
- Note hesitations and confusions
|
||||
- Write down what they say
|
||||
|
||||
**After:**
|
||||
- Ask what was confusing
|
||||
- Ask what they expected
|
||||
- Ask how they'd describe the site
|
||||
|
||||
### Common Findings
|
||||
|
||||
| What Users Do | What It Means |
|
||||
|---------------|---------------|
|
||||
| Click wrong thing | Label is confusing |
|
||||
| Hesitate | Decision isn't obvious |
|
||||
| Look around lost | "You are here" is unclear |
|
||||
| Read everything | Design isn't self-evident |
|
||||
| Use search immediately | Navigation is failing |
|
||||
| Express confusion | Copy is unclear |
|
||||
|
||||
---
|
||||
|
||||
## Accessibility Basics
|
||||
|
||||
### Why It Matters
|
||||
|
||||
- 15-20% of population has some disability
|
||||
- Accessible sites are better for everyone
|
||||
- It's often required by law
|
||||
- It's the right thing to do
|
||||
|
||||
### Quick Wins
|
||||
|
||||
| Fix | Benefit |
|
||||
|-----|---------|
|
||||
| Add alt text to images | Screen readers can describe |
|
||||
| Use sufficient contrast | Low-vision users can read |
|
||||
| Allow keyboard navigation | Motor-impaired users can navigate |
|
||||
| Use semantic HTML | Assistive tech understands structure |
|
||||
| Add focus indicators | Keyboard users know where they are |
|
||||
| Make touch targets large | Everyone benefits |
|
||||
@@ -0,0 +1,360 @@
|
||||
# Nielsen's 10 Usability Heuristics
|
||||
|
||||
Jakob Nielsen's 10 general principles for interaction design, with practical examples and common violations.
|
||||
|
||||
---
|
||||
|
||||
## 1. Visibility of System Status
|
||||
|
||||
**Principle:** The system should always keep users informed about what is going on, through appropriate feedback within reasonable time.
|
||||
|
||||
### Examples of Good Implementation
|
||||
|
||||
| Situation | Good Feedback |
|
||||
|-----------|---------------|
|
||||
| File uploading | Progress bar with percentage |
|
||||
| Form submitted | "Your message has been sent" |
|
||||
| Action processing | Loading spinner |
|
||||
| Background task | "Syncing 3 files..." notification |
|
||||
| Successful action | Green checkmark confirmation |
|
||||
|
||||
### Common Violations
|
||||
|
||||
| Violation | Problem | Fix |
|
||||
|-----------|---------|-----|
|
||||
| No loading indicator | User thinks it's broken | Add spinner/progress |
|
||||
| Silent failures | User thinks action worked | Show error message |
|
||||
| Delayed feedback | User clicks again | Immediate visual response |
|
||||
| No confirmation | "Did that work?" | Confirm successful actions |
|
||||
| Hidden status | User can't find progress | Surface status prominently |
|
||||
|
||||
### Severity Examples
|
||||
|
||||
- **Minor (1):** Save button has no "saved" confirmation
|
||||
- **Major (3):** Payment processing with no indicator
|
||||
- **Catastrophic (4):** Form submit shows nothing, user submits multiple times
|
||||
|
||||
---
|
||||
|
||||
## 2. Match Between System and Real World
|
||||
|
||||
**Principle:** The system should speak the users' language, with words, phrases, and concepts familiar to the user, rather than system-oriented terms.
|
||||
|
||||
### Examples of Good Implementation
|
||||
|
||||
| System Term | User-Friendly Term |
|
||||
|-------------|-------------------|
|
||||
| Authenticate | Sign in |
|
||||
| Terminate | Cancel / End |
|
||||
| Query | Search |
|
||||
| Repository | Folder |
|
||||
| Navigate to | Go to |
|
||||
| Initiate | Start |
|
||||
|
||||
### Real-World Metaphors
|
||||
|
||||
| Digital Element | Real-World Match |
|
||||
|-----------------|------------------|
|
||||
| Trash/Recycle bin | Waste basket |
|
||||
| Folder | File folder |
|
||||
| Desktop | Physical desk |
|
||||
| Shopping cart | Store cart |
|
||||
| Bookmark | Physical bookmark |
|
||||
|
||||
### Common Violations
|
||||
|
||||
| Violation | Problem | Fix |
|
||||
|-----------|---------|-----|
|
||||
| Technical jargon | Confusion | Use plain language |
|
||||
| Internal names | Meaningless to users | User-tested labels |
|
||||
| Inconsistent terms | Same thing, different names | One term per concept |
|
||||
| Unfamiliar icons | Users guess wrong | Add labels or tooltips |
|
||||
| Illogical order | Not matching expectations | Follow real-world sequences |
|
||||
|
||||
---
|
||||
|
||||
## 3. User Control and Freedom
|
||||
|
||||
**Principle:** Users often choose system functions by mistake and need a clearly marked "emergency exit" to leave the unwanted state without having to go through an extended dialogue.
|
||||
|
||||
### Examples of Good Implementation
|
||||
|
||||
| Action | Escape Route |
|
||||
|--------|--------------|
|
||||
| Accidentally deleted email | Undo button (Gmail) |
|
||||
| Wrong menu opened | Click outside to close |
|
||||
| Filled form incorrectly | Clear form / Reset |
|
||||
| Navigated wrong | Back button works |
|
||||
| Started wrong workflow | Cancel / Exit anytime |
|
||||
|
||||
### Common Violations
|
||||
|
||||
| Violation | Problem | Fix |
|
||||
|-----------|---------|-----|
|
||||
| No undo | Users afraid to act | Add undo for all actions |
|
||||
| Forced wizards | Can't skip or go back | Allow non-linear navigation |
|
||||
| Modal traps | Can't escape | Clear close/cancel buttons |
|
||||
| Broken back button | Frustration | Never hijack browser history |
|
||||
| Immediate deletion | No recovery | Soft delete + undo option |
|
||||
|
||||
### Key Principle
|
||||
|
||||
**Undo > Confirmation dialogs**
|
||||
|
||||
Users click through "Are you sure?" without reading. Undo lets them act confidently.
|
||||
|
||||
---
|
||||
|
||||
## 4. Consistency and Standards
|
||||
|
||||
**Principle:** Users should not have to wonder whether different words, situations, or actions mean the same thing. Follow platform conventions.
|
||||
|
||||
### Types of Consistency
|
||||
|
||||
| Type | Example |
|
||||
|------|---------|
|
||||
| **Internal** | Same button style throughout your app |
|
||||
| **External** | Same patterns as other apps |
|
||||
| **Visual** | Same colors mean same things |
|
||||
| **Functional** | Same action = same result |
|
||||
| **Linguistic** | Same terms for same concepts |
|
||||
|
||||
### Platform Conventions
|
||||
|
||||
| Element | Convention |
|
||||
|---------|------------|
|
||||
| Logo | Top left, links to home |
|
||||
| Search | Top right, magnifying glass |
|
||||
| Cart | Top right, shopping cart icon |
|
||||
| Menu (mobile) | Hamburger icon |
|
||||
| Primary action | Right side or bottom of form |
|
||||
| Cancel | Left of primary action (or text link) |
|
||||
|
||||
### Common Violations
|
||||
|
||||
| Violation | Problem | Fix |
|
||||
|-----------|---------|-----|
|
||||
| Different button styles | Confusion about importance | Consistent button hierarchy |
|
||||
| Same word, different meanings | Misunderstanding | One term per concept |
|
||||
| Unexpected link behavior | New tab when expecting same tab | Follow conventions |
|
||||
| Non-standard icons | Guessing game | Use recognized icons |
|
||||
| Inconsistent layouts | Relearning each page | Template-based layouts |
|
||||
|
||||
---
|
||||
|
||||
## 5. Error Prevention
|
||||
|
||||
**Principle:** Even better than good error messages is a careful design which prevents a problem from occurring in the first place.
|
||||
|
||||
### Prevention Strategies
|
||||
|
||||
| Strategy | Example |
|
||||
|----------|---------|
|
||||
| **Constraints** | Date picker instead of text field |
|
||||
| **Suggestions** | Autocomplete |
|
||||
| **Defaults** | Pre-fill common values |
|
||||
| **Confirmation** | "Delete permanently?" for destructive actions |
|
||||
| **Warnings** | "Unsaved changes" before leaving |
|
||||
|
||||
### Types of Errors to Prevent
|
||||
|
||||
| Error Type | Prevention |
|
||||
|------------|------------|
|
||||
| **Slips** (accidental) | Confirmation, undo, large targets |
|
||||
| **Mistakes** (wrong intention) | Clear instructions, better defaults |
|
||||
| **Data errors** | Validation, formatting help |
|
||||
| **Navigation errors** | Clear labels, undo |
|
||||
|
||||
### Common Violations
|
||||
|
||||
| Violation | Problem | Fix |
|
||||
|-----------|---------|-----|
|
||||
| Free text for constrained data | Invalid entries | Dropdowns, pickers |
|
||||
| No save warning | Lost work | "Unsaved changes" prompt |
|
||||
| Easy destructive actions | Accidental deletion | Require confirmation |
|
||||
| Accepting bad input | Garbage data | Inline validation |
|
||||
| Ambiguous choices | Wrong selection | Clear differentiation |
|
||||
|
||||
---
|
||||
|
||||
## 6. Recognition Rather Than Recall
|
||||
|
||||
**Principle:** Minimize the user's memory load by making objects, actions, and options visible. Don't require users to remember information.
|
||||
|
||||
### Recognition Techniques
|
||||
|
||||
| Instead of | Do This |
|
||||
|------------|---------|
|
||||
| User remembers command | Show menu of options |
|
||||
| User types from memory | Dropdown/autocomplete |
|
||||
| User remembers where they were | Breadcrumbs, recent history |
|
||||
| User remembers codes | Show decoded values |
|
||||
| User recalls previous info | Show previous entries |
|
||||
|
||||
### Examples
|
||||
|
||||
| Bad (Recall) | Good (Recognition) |
|
||||
|--------------|-------------------|
|
||||
| "Enter country code" | Dropdown with country names |
|
||||
| Command-line interface | Graphical menus |
|
||||
| "See page 47 for options" | Options shown in context |
|
||||
| "Re-enter your email" | Pre-filled from previous step |
|
||||
| Complex keyboard shortcuts | Visible toolbar buttons |
|
||||
|
||||
### Common Violations
|
||||
|
||||
| Violation | Problem | Fix |
|
||||
|-----------|---------|-----|
|
||||
| Empty form fields | User must remember format | Placeholder examples |
|
||||
| Hidden actions | User forgets they exist | Keep visible or in menus |
|
||||
| No recent items | User re-searches | Show search history |
|
||||
| Unlabeled icons | User guesses meaning | Add text labels |
|
||||
| Disconnected workflows | User loses context | Show progress, breadcrumbs |
|
||||
|
||||
---
|
||||
|
||||
## 7. Flexibility and Efficiency of Use
|
||||
|
||||
**Principle:** Accelerators—unseen by the novice user—may often speed up the interaction for the expert user. Allow users to tailor frequent actions.
|
||||
|
||||
### Accelerators for Experts
|
||||
|
||||
| Feature | Example |
|
||||
|---------|---------|
|
||||
| **Keyboard shortcuts** | Ctrl+S to save |
|
||||
| **Touch gestures** | Swipe to archive |
|
||||
| **Recent/Favorites** | Quick access to common items |
|
||||
| **Saved searches** | One-click complex queries |
|
||||
| **Customization** | Personalized dashboard |
|
||||
| **Bulk actions** | Select all + action |
|
||||
|
||||
### Progressive Disclosure
|
||||
|
||||
| User Level | What They See |
|
||||
|------------|---------------|
|
||||
| Novice | Essential features only |
|
||||
| Intermediate | Common advanced options |
|
||||
| Expert | Full power (shortcuts, customization) |
|
||||
|
||||
### Common Violations
|
||||
|
||||
| Violation | Problem | Fix |
|
||||
|-----------|---------|-----|
|
||||
| No shortcuts | Experts slowed down | Add keyboard shortcuts |
|
||||
| No bulk operations | Tedious repetition | Add multi-select |
|
||||
| Required tutorials | Experts frustrated | Allow skipping |
|
||||
| Hidden power features | Experts don't find them | Discoverable advanced mode |
|
||||
| No customization | Forced workflows | Allow personalization |
|
||||
|
||||
---
|
||||
|
||||
## 8. Aesthetic and Minimalist Design
|
||||
|
||||
**Principle:** Dialogues should not contain information which is irrelevant or rarely needed. Every extra unit of information competes with the relevant units.
|
||||
|
||||
### Principles
|
||||
|
||||
| Principle | Application |
|
||||
|-----------|-------------|
|
||||
| **Signal/Noise** | Increase signal, reduce noise |
|
||||
| **Visual hierarchy** | Important things stand out |
|
||||
| **Whitespace** | Give elements room to breathe |
|
||||
| **Content priority** | Show what matters, hide what doesn't |
|
||||
| **Progressive disclosure** | Complexity on demand |
|
||||
|
||||
### What to Remove
|
||||
|
||||
| Remove | Why |
|
||||
|--------|-----|
|
||||
| Rarely-used features | Clutter |
|
||||
| Decorative elements | Distraction |
|
||||
| Redundant text | Noise |
|
||||
| Unnecessary options | Decision fatigue |
|
||||
| Instructions users skip | Wasted space |
|
||||
|
||||
### Common Violations
|
||||
|
||||
| Violation | Problem | Fix |
|
||||
|-----------|---------|-----|
|
||||
| Cluttered screens | Overwhelming | Remove/hide secondary |
|
||||
| Everything is "important" | Nothing stands out | Create hierarchy |
|
||||
| Long blocks of text | Nobody reads | Break up, summarize |
|
||||
| Too many colors | Visual noise | Limit palette |
|
||||
| Dense layouts | Hard to scan | Add whitespace |
|
||||
|
||||
---
|
||||
|
||||
## 9. Help Users Recognize, Diagnose, and Recover from Errors
|
||||
|
||||
**Principle:** Error messages should be expressed in plain language (no codes), precisely indicate the problem, and constructively suggest a solution.
|
||||
|
||||
### Good Error Message Components
|
||||
|
||||
1. **What happened** (plain language)
|
||||
2. **Why it happened** (if helpful)
|
||||
3. **How to fix it** (specific action)
|
||||
|
||||
### Examples
|
||||
|
||||
| Bad Error | Good Error |
|
||||
|-----------|------------|
|
||||
| "Error 500" | "Something went wrong. Please try again." |
|
||||
| "Invalid input" | "Email must include @" |
|
||||
| "Failed" | "Payment declined. Check card number or try different card." |
|
||||
| "Null reference exception" | "We couldn't load your data. Refresh the page." |
|
||||
|
||||
### Error Message Guidelines
|
||||
|
||||
| Guideline | Example |
|
||||
|-----------|---------|
|
||||
| Use plain language | "Connection failed" not "ECONNREFUSED" |
|
||||
| Be specific | "Password too short" not "Invalid password" |
|
||||
| Provide action | "Try again" button visible |
|
||||
| Don't blame user | "Card declined" not "You entered wrong info" |
|
||||
| Maintain context | Keep filled data, highlight error field |
|
||||
|
||||
### Common Violations
|
||||
|
||||
| Violation | Problem | Fix |
|
||||
|-----------|---------|-----|
|
||||
| Technical jargon | Confusion | Translate to plain English |
|
||||
| No solution | User stuck | Include next steps |
|
||||
| Generic messages | Not helpful | Be specific |
|
||||
| Blaming language | Defensive users | Neutral, helpful tone |
|
||||
| Clearing form on error | Punishment | Preserve user input |
|
||||
|
||||
---
|
||||
|
||||
## 10. Help and Documentation
|
||||
|
||||
**Principle:** Even though it's better if the system can be used without documentation, it may be necessary to provide help and documentation. Such information should be easy to search, focused on the user's task, and not be too large.
|
||||
|
||||
### Characteristics of Good Help
|
||||
|
||||
| Characteristic | Implementation |
|
||||
|----------------|----------------|
|
||||
| **Searchable** | Full-text search |
|
||||
| **Task-focused** | "How to..." format |
|
||||
| **Contextual** | In-page tooltips |
|
||||
| **Scannable** | Short paragraphs, lists |
|
||||
| **Actionable** | Step-by-step instructions |
|
||||
|
||||
### Types of Help
|
||||
|
||||
| Type | When to Use |
|
||||
|------|-------------|
|
||||
| **Inline help** | Tooltips, hints | Next to complex fields |
|
||||
| **Contextual help** | "?" icons | For non-obvious features |
|
||||
| **Searchable docs** | Knowledge base | For detailed questions |
|
||||
| **Guided tours** | Onboarding | First-time users |
|
||||
| **Chat/Support** | Complex issues | When self-service fails |
|
||||
|
||||
### Common Violations
|
||||
|
||||
| Violation | Problem | Fix |
|
||||
|-----------|---------|-----|
|
||||
| No search in docs | Can't find answers | Add search |
|
||||
| Long documentation | Nobody reads | Concise, task-focused |
|
||||
| Generic help | Doesn't answer question | Specific to feature/page |
|
||||
| Hidden help | Users can't find it | Visible help links |
|
||||
| No contextual help | Users leave page | Inline tooltips |
|
||||
@@ -0,0 +1,306 @@
|
||||
# WCAG 2.1 AA Checklist
|
||||
|
||||
Complete checklist for WCAG 2.1 Level AA compliance with testing guidance.
|
||||
|
||||
## Perceivable
|
||||
|
||||
Content must be presentable in ways users can perceive.
|
||||
|
||||
### 1.1 Text Alternatives
|
||||
|
||||
| Criterion | Requirement | How to Test |
|
||||
|-----------|-------------|-------------|
|
||||
| 1.1.1 Non-text Content | All images, icons, and visual content have text alternatives | Inspect alt attributes; use screen reader |
|
||||
|
||||
**Pass criteria:**
|
||||
- [ ] Informative images have descriptive alt text
|
||||
- [ ] Decorative images have empty alt (`alt=""`)
|
||||
- [ ] Complex images (charts, diagrams) have extended descriptions
|
||||
- [ ] Icons have accessible names
|
||||
- [ ] CAPTCHA provides audio alternative
|
||||
|
||||
### 1.2 Time-based Media
|
||||
|
||||
| Criterion | Requirement | How to Test |
|
||||
|-----------|-------------|-------------|
|
||||
| 1.2.1 Audio/Video (prerecorded) | Captions and/or transcripts | Check video player for captions |
|
||||
| 1.2.2 Captions | Synchronized captions for video | Watch with captions on |
|
||||
| 1.2.3 Audio Description | Description of visual content | Check for AD track |
|
||||
| 1.2.5 Audio Description (AA) | Audio description for all video | Verify AD available |
|
||||
|
||||
**Pass criteria:**
|
||||
- [ ] Videos have synchronized captions
|
||||
- [ ] Captions are accurate and complete
|
||||
- [ ] Audio descriptions available for important visual content
|
||||
- [ ] Transcripts available for audio-only content
|
||||
|
||||
### 1.3 Adaptable
|
||||
|
||||
| Criterion | Requirement | How to Test |
|
||||
|-----------|-------------|-------------|
|
||||
| 1.3.1 Info and Relationships | Semantic structure preserved | Inspect HTML; use screen reader |
|
||||
| 1.3.2 Meaningful Sequence | Content order makes sense | Disable CSS; read in DOM order |
|
||||
| 1.3.3 Sensory Characteristics | Don't rely on shape/color/position alone | Check instructions |
|
||||
| 1.3.4 Orientation (AA) | Works in portrait and landscape | Rotate device |
|
||||
| 1.3.5 Identify Input Purpose (AA) | Input fields have autocomplete | Check `autocomplete` attributes |
|
||||
|
||||
**Pass criteria:**
|
||||
- [ ] Headings use proper h1-h6 hierarchy
|
||||
- [ ] Lists use `<ul>`, `<ol>`, `<dl>` elements
|
||||
- [ ] Forms have proper labels associated with inputs
|
||||
- [ ] Tables have headers marked with `<th>`
|
||||
- [ ] Reading order is logical without CSS
|
||||
- [ ] Works in both orientations
|
||||
- [ ] Input fields have appropriate autocomplete values
|
||||
|
||||
### 1.4 Distinguishable
|
||||
|
||||
| Criterion | Requirement | How to Test |
|
||||
|-----------|-------------|-------------|
|
||||
| 1.4.1 Use of Color | Color not sole means of info | View in grayscale |
|
||||
| 1.4.2 Audio Control | Auto-playing audio can be stopped | Check for controls |
|
||||
| 1.4.3 Contrast (Minimum) | 4.5:1 text, 3:1 large text | Use contrast checker |
|
||||
| 1.4.4 Resize Text | Readable at 200% zoom | Zoom browser |
|
||||
| 1.4.5 Images of Text | Use real text, not images | Inspect for text images |
|
||||
| 1.4.10 Reflow (AA) | No horizontal scroll at 320px | Test at narrow width |
|
||||
| 1.4.11 Non-text Contrast (AA) | 3:1 for UI components | Check buttons, inputs |
|
||||
| 1.4.12 Text Spacing (AA) | Survives increased text spacing | Apply spacing override |
|
||||
| 1.4.13 Content on Hover/Focus (AA) | Hoverable, dismissible, persistent | Test tooltips, menus |
|
||||
|
||||
**Pass criteria:**
|
||||
- [ ] Normal text has 4.5:1 contrast ratio minimum
|
||||
- [ ] Large text (18pt+) has 3:1 contrast ratio minimum
|
||||
- [ ] UI components have 3:1 contrast
|
||||
- [ ] Text resizes to 200% without loss
|
||||
- [ ] No horizontal scrolling at 320px width
|
||||
- [ ] Tooltips are dismissible, hoverable, persistent
|
||||
- [ ] Color alone doesn't convey meaning
|
||||
|
||||
---
|
||||
|
||||
## Operable
|
||||
|
||||
Users must be able to operate the interface.
|
||||
|
||||
### 2.1 Keyboard Accessible
|
||||
|
||||
| Criterion | Requirement | How to Test |
|
||||
|-----------|-------------|-------------|
|
||||
| 2.1.1 Keyboard | All functionality works with keyboard | Navigate with Tab, Enter, Space |
|
||||
| 2.1.2 No Keyboard Trap | Focus can always move away | Tab through everything |
|
||||
| 2.1.4 Character Key Shortcuts (AA) | Single-key shortcuts can be disabled | Check for shortcut conflicts |
|
||||
|
||||
**Pass criteria:**
|
||||
- [ ] All interactive elements are focusable
|
||||
- [ ] All actions can be performed via keyboard
|
||||
- [ ] Focus is never trapped
|
||||
- [ ] Single-key shortcuts can be disabled or remapped
|
||||
- [ ] Tab order follows logical sequence
|
||||
|
||||
### 2.2 Enough Time
|
||||
|
||||
| Criterion | Requirement | How to Test |
|
||||
|-----------|-------------|-------------|
|
||||
| 2.2.1 Timing Adjustable | Users can extend/disable time limits | Check for timeouts |
|
||||
| 2.2.2 Pause, Stop, Hide | Auto-updating content can be controlled | Check carousels, animations |
|
||||
|
||||
**Pass criteria:**
|
||||
- [ ] Session timeouts have 20-second warning
|
||||
- [ ] Users can extend time limits
|
||||
- [ ] Auto-playing content has pause control
|
||||
- [ ] Animations can be stopped
|
||||
|
||||
### 2.3 Seizures
|
||||
|
||||
| Criterion | Requirement | How to Test |
|
||||
|-----------|-------------|-------------|
|
||||
| 2.3.1 Three Flashes | No content flashes >3 times/second | Measure flash rate |
|
||||
|
||||
**Pass criteria:**
|
||||
- [ ] No flashing content above threshold
|
||||
- [ ] Animations don't cause seizure risk
|
||||
|
||||
### 2.4 Navigable
|
||||
|
||||
| Criterion | Requirement | How to Test |
|
||||
|-----------|-------------|-------------|
|
||||
| 2.4.1 Bypass Blocks | Skip links to bypass repeated content | Check for skip link |
|
||||
| 2.4.2 Page Titled | Pages have descriptive titles | Check `<title>` element |
|
||||
| 2.4.3 Focus Order | Focus sequence is logical | Tab through page |
|
||||
| 2.4.4 Link Purpose | Link text describes destination | Read links out of context |
|
||||
| 2.4.5 Multiple Ways (AA) | Multiple ways to find pages | Check nav, search, sitemap |
|
||||
| 2.4.6 Headings and Labels (AA) | Headings and labels are descriptive | Review all headings |
|
||||
| 2.4.7 Focus Visible (AA) | Focus indicator is visible | Tab through interface |
|
||||
|
||||
**Pass criteria:**
|
||||
- [ ] Skip link present at top of page
|
||||
- [ ] Page titles are unique and descriptive
|
||||
- [ ] Tab order follows visual order
|
||||
- [ ] Link text is meaningful ("Read more about X" not "Click here")
|
||||
- [ ] Multiple navigation methods available
|
||||
- [ ] All headings are descriptive
|
||||
- [ ] Focus indicator is clearly visible
|
||||
|
||||
### 2.5 Input Modalities
|
||||
|
||||
| Criterion | Requirement | How to Test |
|
||||
|-----------|-------------|-------------|
|
||||
| 2.5.1 Pointer Gestures (AA) | Complex gestures have simple alternatives | Check for alternatives |
|
||||
| 2.5.2 Pointer Cancellation (AA) | Actions on up-event, cancelable | Test click/drag behavior |
|
||||
| 2.5.3 Label in Name (AA) | Accessible name contains visible label | Compare visible/accessible names |
|
||||
| 2.5.4 Motion Actuation (AA) | Motion-based actions have alternatives | Check for non-motion options |
|
||||
|
||||
**Pass criteria:**
|
||||
- [ ] Pinch, swipe have tap alternatives
|
||||
- [ ] Click actions happen on release (up-event)
|
||||
- [ ] Dragging outside target cancels action
|
||||
- [ ] Accessible names include visible text
|
||||
- [ ] Shake/tilt actions have button alternatives
|
||||
|
||||
---
|
||||
|
||||
## Understandable
|
||||
|
||||
Content must be understandable to users.
|
||||
|
||||
### 3.1 Readable
|
||||
|
||||
| Criterion | Requirement | How to Test |
|
||||
|-----------|-------------|-------------|
|
||||
| 3.1.1 Language of Page | Page language specified | Check `<html lang="">` |
|
||||
| 3.1.2 Language of Parts (AA) | Foreign text marked with lang | Check multilingual content |
|
||||
|
||||
**Pass criteria:**
|
||||
- [ ] HTML has lang attribute
|
||||
- [ ] Foreign language passages have lang attribute
|
||||
|
||||
### 3.2 Predictable
|
||||
|
||||
| Criterion | Requirement | How to Test |
|
||||
|-----------|-------------|-------------|
|
||||
| 3.2.1 On Focus | Focus doesn't cause unexpected changes | Tab through all elements |
|
||||
| 3.2.2 On Input | Input doesn't cause unexpected changes | Fill forms without submitting |
|
||||
| 3.2.3 Consistent Navigation (AA) | Navigation is consistent across pages | Compare pages |
|
||||
| 3.2.4 Consistent Identification (AA) | Same functions have same labels | Compare repeated elements |
|
||||
|
||||
**Pass criteria:**
|
||||
- [ ] Focus doesn't trigger context changes
|
||||
- [ ] Selecting options doesn't submit forms
|
||||
- [ ] Navigation is in same location on all pages
|
||||
- [ ] Same icons/labels used for same functions
|
||||
|
||||
### 3.3 Input Assistance
|
||||
|
||||
| Criterion | Requirement | How to Test |
|
||||
|-----------|-------------|-------------|
|
||||
| 3.3.1 Error Identification | Errors clearly identified | Submit invalid forms |
|
||||
| 3.3.2 Labels or Instructions | Labels and instructions provided | Review all forms |
|
||||
| 3.3.3 Error Suggestion (AA) | Suggestions for fixing errors | Trigger errors |
|
||||
| 3.3.4 Error Prevention (AA) | Confirmation for legal/financial actions | Test critical submissions |
|
||||
|
||||
**Pass criteria:**
|
||||
- [ ] Errors are clearly described in text
|
||||
- [ ] Error messages explain how to fix
|
||||
- [ ] All inputs have visible labels
|
||||
- [ ] Required fields are indicated
|
||||
- [ ] Legal/financial submissions are reversible or confirmed
|
||||
|
||||
---
|
||||
|
||||
## Robust
|
||||
|
||||
Content must work with assistive technologies.
|
||||
|
||||
### 4.1 Compatible
|
||||
|
||||
| Criterion | Requirement | How to Test |
|
||||
|-----------|-------------|-------------|
|
||||
| 4.1.1 Parsing | Valid HTML | Run HTML validator |
|
||||
| 4.1.2 Name, Role, Value | ARIA used correctly | Inspect with accessibility tools |
|
||||
| 4.1.3 Status Messages (AA) | Status updates announced | Test with screen reader |
|
||||
|
||||
**Pass criteria:**
|
||||
- [ ] HTML is valid (no duplicate IDs, proper nesting)
|
||||
- [ ] Custom controls have appropriate roles
|
||||
- [ ] States (expanded, selected) are programmatically set
|
||||
- [ ] Status messages use aria-live regions
|
||||
|
||||
---
|
||||
|
||||
## Testing Tools
|
||||
|
||||
### Automated Testing
|
||||
|
||||
| Tool | What It Catches | Use For |
|
||||
|------|-----------------|---------|
|
||||
| axe DevTools | ~30% of issues | Quick scan |
|
||||
| WAVE | Similar to axe | Visual overlay |
|
||||
| Lighthouse | Basic accessibility | CI/CD integration |
|
||||
| Pa11y | CLI-based | Automated testing |
|
||||
|
||||
### Manual Testing Required
|
||||
|
||||
| Area | How to Test |
|
||||
|------|-------------|
|
||||
| Keyboard navigation | Unplug mouse, use only keyboard |
|
||||
| Screen reader | VoiceOver (Mac), NVDA (Windows) |
|
||||
| Zoom | Browser zoom to 200%, 400% |
|
||||
| Color contrast | WebAIM Contrast Checker |
|
||||
| Color blindness | Sim Daltonism, Chrome DevTools |
|
||||
|
||||
### Screen Reader Testing
|
||||
|
||||
**VoiceOver (Mac):**
|
||||
1. Enable: Cmd + F5
|
||||
2. Navigate: VO (Ctrl + Opt) + arrows
|
||||
3. Rotor: VO + U (headings, links, forms)
|
||||
|
||||
**NVDA (Windows):**
|
||||
1. Download free from nvaccess.org
|
||||
2. Navigate: Arrow keys, Tab
|
||||
3. Elements list: NVDA + F7
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference: Common Failures
|
||||
|
||||
| Issue | Failure | Fix |
|
||||
|-------|---------|-----|
|
||||
| Missing alt text | 1.1.1 | Add descriptive alt or `alt=""` |
|
||||
| Low contrast | 1.4.3 | Increase to 4.5:1 (text) or 3:1 (large/UI) |
|
||||
| No focus indicator | 2.4.7 | Add visible :focus-visible styles |
|
||||
| Keyboard inaccessible | 2.1.1 | Make interactive, add tabindex=0 |
|
||||
| Missing form labels | 1.3.1, 3.3.2 | Add `<label>` with `for` attribute |
|
||||
| No skip link | 2.4.1 | Add "Skip to content" link |
|
||||
| Missing page title | 2.4.2 | Add descriptive `<title>` |
|
||||
| Color-only meaning | 1.4.1 | Add icon or text alongside |
|
||||
| No lang attribute | 3.1.1 | Add `<html lang="en">` |
|
||||
| Ambiguous links | 2.4.4 | Use descriptive link text |
|
||||
|
||||
---
|
||||
|
||||
## Testing Checklist Summary
|
||||
|
||||
Before launch, verify:
|
||||
|
||||
**Automated scan:**
|
||||
- [ ] axe DevTools shows 0 issues
|
||||
- [ ] Lighthouse accessibility score >90
|
||||
|
||||
**Keyboard:**
|
||||
- [ ] All functionality works with keyboard
|
||||
- [ ] Focus visible on all elements
|
||||
- [ ] No keyboard traps
|
||||
- [ ] Skip link works
|
||||
|
||||
**Screen reader:**
|
||||
- [ ] All content announced correctly
|
||||
- [ ] Form fields have labels
|
||||
- [ ] Images have alt text
|
||||
- [ ] Headings create logical outline
|
||||
|
||||
**Visual:**
|
||||
- [ ] All text meets contrast requirements
|
||||
- [ ] Works at 200% zoom
|
||||
- [ ] Works at 320px width (mobile)
|
||||
- [ ] Color not sole indicator
|
||||
@@ -0,0 +1,301 @@
|
||||
---
|
||||
name: ux-heuristics
|
||||
description: 'Evaluate and improve interface usability using heuristic analysis. Use when the user mentions "usability audit", "UX review", "users are confused", "heuristic evaluation", "form usability", or "navigation problems". Covers Nielsen''s 10 heuristics, severity ratings, and information architecture. For visual design fixes, see refactoring-ui. For conversion-focused audits, see cro-methodology.'
|
||||
license: MIT
|
||||
metadata:
|
||||
author: wondelai
|
||||
version: "1.2.1"
|
||||
---
|
||||
|
||||
# UX Heuristics Framework
|
||||
|
||||
Practical usability principles for evaluating and improving user interfaces. Based on a fundamental truth: users don't read, they scan. They don't make optimal choices, they satisfice. They don't figure out how things work, they muddle through.
|
||||
|
||||
## Core Principle
|
||||
|
||||
**"Don't Make Me Think"** - Every page should be self-evident. If something requires thinking, it's a usability problem.
|
||||
|
||||
**The foundation:** Users have limited patience and cognitive bandwidth. The best interfaces are invisible -- they let users accomplish goals without ever stopping to wonder "What do I click?" or "Where am I?" Every question mark that pops into a user's head adds to cognitive load and increases the chance they'll leave. Design for scanning, satisficing, and muddling through -- because that's what users actually do.
|
||||
|
||||
## Scoring
|
||||
|
||||
**Goal: 10/10.** When reviewing or creating user interfaces, rate them 0-10 based on adherence to the principles below. A 10/10 means full alignment with all guidelines; lower scores indicate gaps to address. Always provide the current score and specific improvements needed to reach 10/10.
|
||||
|
||||
## Krug's Three Laws of Usability
|
||||
|
||||
### 1. Don't Make Me Think
|
||||
|
||||
**Core concept:** Every question mark that pops into a user's head adds to their cognitive load and distracts from the task.
|
||||
|
||||
**Why it works:** Users are on a mission. They don't want to puzzle over labels, wonder what a link does, or decode clever marketing language. The less thinking required, the more likely they complete the task.
|
||||
|
||||
**Key insights:**
|
||||
- Clever names lose to clear names every time
|
||||
- Marketing-speak creates friction; plain language removes it
|
||||
- Unfamiliar categories and labels force users to stop and interpret
|
||||
- Links that could go anywhere create uncertainty
|
||||
- Buttons with ambiguous labels cause hesitation
|
||||
|
||||
**Product applications:**
|
||||
|
||||
| Context | Application | Example |
|
||||
|---------|-------------|---------|
|
||||
| **Navigation labels** | Use self-evident names | "Get directions" not "Calculate route to destination" |
|
||||
| **CTAs** | Use action verbs users understand | "Sign in" not "Access your account portal" |
|
||||
| **E-commerce** | Match user mental models | "Add to cart" not "Proceed to purchase selection" |
|
||||
| **Form labels** | Describe what's needed plainly | "Email address" not "Electronic correspondence identifier" |
|
||||
| **Error states** | Tell users what to do next | "Check your email format" not "Validation error" |
|
||||
|
||||
**Copy patterns:**
|
||||
- Self-evident labels: "Sign in", "Search", "Add to cart"
|
||||
- Action-oriented buttons: verb + noun ("Create account", "Download report")
|
||||
- Avoid jargon: "Save" not "Persist", "Remove" not "Disassociate"
|
||||
- If a label needs explanation, simplify the label
|
||||
|
||||
**Ethical boundary:** Clarity should serve users, not obscure information. Never use plain language as a veneer to hide unfavorable terms.
|
||||
|
||||
See: [references/krug-principles.md](references/krug-principles.md) for full Krug methodology.
|
||||
|
||||
### 2. It Doesn't Matter How Many Clicks
|
||||
|
||||
**Core concept:** The myth says "users leave after 3 clicks." The reality is users don't mind clicks if each one is painless, obvious, and confidence-building.
|
||||
|
||||
**Why it works:** Cognitive effort per click matters more than click count. Three mindless, confident clicks are far better than one click that requires deliberation. Users abandon when they lose confidence, not when they run out of patience for clicking.
|
||||
|
||||
**Key insights:**
|
||||
- Each click should be painless (fast, easy)
|
||||
- Each click should be obvious (no thinking required)
|
||||
- Each click should build confidence (users know they're on the right path)
|
||||
- Three mindless clicks beat one confusing click every time
|
||||
- Users abandon when confused, not when they've clicked too many times
|
||||
|
||||
**Product applications:**
|
||||
|
||||
| Context | Application | Example |
|
||||
|---------|-------------|---------|
|
||||
| **Information architecture** | Prioritize clarity over depth | Shallow nav with clear labels over deep nav with vague ones |
|
||||
| **Checkout flows** | Make each step obvious | Clear step indicators with descriptive labels |
|
||||
| **Settings** | Organize into clear categories | "Account > Security > Change password" (3 confident clicks) |
|
||||
| **Search results** | Let users drill down confidently | Category filters that narrow results progressively |
|
||||
| **Onboarding** | Guide with small, clear steps | Wizard with one clear action per step |
|
||||
|
||||
**Copy patterns:**
|
||||
- Progress indicators: "Step 2 of 4: Shipping details"
|
||||
- Breadcrumbs: "Home > Products > Shoes > Running"
|
||||
- Confirmations at each step: "Great, your email is verified. Now let's set up your profile."
|
||||
- Clear link text: "View all running shoes" not "Click here"
|
||||
|
||||
**Ethical boundary:** Don't use extra steps to bury cancellation flows or make opting out harder. Every click should move users toward their goal, not away from it.
|
||||
|
||||
See: [references/krug-principles.md](references/krug-principles.md) for Krug's click philosophy and scanning behavior.
|
||||
|
||||
### 3. Get Rid of Half the Words
|
||||
|
||||
**Core concept:** Get rid of half the words on each page, then get rid of half of what's left. Brevity reduces noise, makes useful content more prominent, and shows respect for the user's time.
|
||||
|
||||
**Why it works:** Users scan -- they don't read. Every unnecessary word competes with the words that matter. Removing fluff makes important content more discoverable and pages shorter.
|
||||
|
||||
**Key insights:**
|
||||
- Happy-talk ("Welcome to our website!") wastes space
|
||||
- Instructions nobody reads should be removed
|
||||
- "Please" and "Kindly" and polite fluff add noise
|
||||
- Redundant explanations dilute the message
|
||||
- Shorter pages mean less scrolling and faster scanning
|
||||
|
||||
**Product applications:**
|
||||
|
||||
| Context | Application | Example |
|
||||
|---------|-------------|---------|
|
||||
| **Landing pages** | Cut welcome copy, lead with value | Remove "Welcome to..." paragraphs |
|
||||
| **Error messages** | State problem and fix, nothing more | "Password too short (min 8 chars)" not a paragraph |
|
||||
| **Tooltips** | One sentence max | "Last 4 digits of your card" not a full explanation |
|
||||
| **Empty states** | Action-oriented, minimal | "No results. Try a different search." |
|
||||
| **Onboarding** | One instruction per screen | "Choose your interests" not a wall of explanatory text |
|
||||
|
||||
**Copy patterns:**
|
||||
- Before: "Please kindly note that you will need to enter your password in order to proceed to the next step."
|
||||
- After: "Enter your password to continue."
|
||||
- Before: "We've received your message and will get back to you as soon as possible."
|
||||
- After: "Message sent. We'll reply within 24 hours."
|
||||
|
||||
**Ethical boundary:** Brevity must not mean omitting critical information. Concise disclosures for pricing, terms, and data usage are a user right.
|
||||
|
||||
See: [references/krug-principles.md](references/krug-principles.md) for Krug's word-cutting methodology.
|
||||
|
||||
### 4. The Trunk Test
|
||||
|
||||
**Core concept:** A test for navigation clarity: if users were dropped on any random page (like being locked in a car trunk and released at a random spot), could they instantly answer six key questions?
|
||||
|
||||
**Why it works:** Good navigation gives users constant orientation. If users can't identify where they are and what their options are, they feel lost and leave.
|
||||
|
||||
**Key insights:**
|
||||
- Users must know what site they're on (brand/logo visible)
|
||||
- Users must know what page they're on (clear heading)
|
||||
- Major sections must be visible (navigation)
|
||||
- Options at this level must be clear (links/buttons)
|
||||
- Position in hierarchy must be apparent (breadcrumbs)
|
||||
- Search must be findable
|
||||
|
||||
**Product applications:**
|
||||
|
||||
| Context | Application | Example |
|
||||
|---------|-------------|---------|
|
||||
| **Global nav** | Persistent site ID and sections | Logo top-left, main nav always visible |
|
||||
| **Page headers** | Clear, descriptive page titles | "Running Shoes - Men's" not just "Products" |
|
||||
| **Breadcrumbs** | Show hierarchy on all inner pages | "Home > Products > Shoes > Running" |
|
||||
| **Mobile nav** | Maintain orientation in hamburger menus | Highlight current section, show breadcrumbs |
|
||||
| **Search** | Visible search on every page | Search box in header, not buried in footer |
|
||||
|
||||
**Copy patterns:**
|
||||
- Page titles that match the link the user clicked
|
||||
- "You are here" indicators (highlighted nav items, bold breadcrumb)
|
||||
- Section headings that orient: "Your Account > Billing" not just "Settings"
|
||||
- Footer navigation for secondary discovery
|
||||
|
||||
**Ethical boundary:** Navigation should honestly represent site structure. Don't use misleading labels to funnel users into marketing pages.
|
||||
|
||||
See: [references/krug-principles.md](references/krug-principles.md) for the full Trunk Test methodology.
|
||||
|
||||
## Nielsen's 10 Usability Heuristics
|
||||
|
||||
### 1. Visibility of System Status
|
||||
Keep users informed about what's happening through timely feedback. Every action needs acknowledgment — progress bars for uploads, confirmations for submissions, skeleton screens for loading. Silent failures destroy trust. Copy pattern: "Saving..." → "Saved" (immediate state transitions).
|
||||
|
||||
### 2. Match Between System and Real World
|
||||
Speak users' language, not system language. Use "Sign in" not "Authenticate", "Search" not "Query." Follow real-world metaphors (trash bin, shopping cart) and natural ordering (street → city → state → zip). One term per concept, everywhere.
|
||||
|
||||
### 3. User Control and Freedom
|
||||
Provide clear "emergency exits." Undo beats "Are you sure?" dialogs every time — users click through confirmations without reading. Every flow needs cancel/exit, back buttons must never break, and soft delete with undo beats permanent deletion.
|
||||
|
||||
### 4. Consistency and Standards
|
||||
Same words, styles, and behaviors should mean the same thing throughout. Internal consistency (your app) and external consistency (platform conventions: logo top-left, search top-right). Pick one term per concept — "Projects" everywhere, never mixing with "Workspaces."
|
||||
|
||||
### 5. Error Prevention
|
||||
Prevent problems before they occur. Constrained inputs (date pickers over text fields), autocomplete, sensible defaults, and "unsaved changes" warnings. Two error types need different prevention: slips (accidental wrong action) and mistakes (wrong intention).
|
||||
|
||||
### 6. Recognition Rather Than Recall
|
||||
Minimize memory load — show options, don't require memorization. Breadcrumbs, recent searches, pre-filled fields, dropdowns with decoded values (country names, not codes). Human working memory holds ~7 items; recognition is far easier than recall.
|
||||
|
||||
### 7. Flexibility and Efficiency of Use
|
||||
Serve both novices and experts. Keyboard shortcuts, touch gestures, bulk actions, saved searches, and command palettes (Cmd+K) speed up power users. Progressive disclosure keeps it simple for beginners while experts access full power.
|
||||
|
||||
### 8. Aesthetic and Minimalist Design
|
||||
Every element must earn its place. Signal-to-noise ratio determines usability — when everything screams for attention, nothing stands out. Show what matters now, hide what doesn't. One primary CTA per page, not five competing ones.
|
||||
|
||||
### 9. Help Users Recognize, Diagnose, and Recover from Errors
|
||||
Error messages need three parts: what happened, why, and how to fix it. Plain language always ("Connection failed" not "ECONNREFUSED"), specific ("Password must be 8+ characters" not "Invalid"), never blame the user, and preserve their input.
|
||||
|
||||
### 10. Help and Documentation
|
||||
Help should be searchable, task-focused ("How to..." not technical reference), and contextual (tooltips, inline hints). Types: inline help, contextual "?" icons, searchable knowledge base, guided tours, live support.
|
||||
|
||||
See: [references/nielsen-heuristics.md](references/nielsen-heuristics.md) for detailed examples, product applications, copy patterns, and ethical boundaries for all 10 heuristics.
|
||||
|
||||
## Severity Rating Scale
|
||||
|
||||
When auditing interfaces, rate each issue:
|
||||
|
||||
| Severity | Rating | Description | Priority |
|
||||
|----------|--------|-------------|----------|
|
||||
| **0** | Not a problem | Disagreement, not usability issue | Ignore |
|
||||
| **1** | Cosmetic | Minor annoyance, low impact | Fix if time |
|
||||
| **2** | Minor | Causes delay or frustration | Schedule fix |
|
||||
| **3** | Major | Significant task failure | Fix soon |
|
||||
| **4** | Catastrophic | Prevents task completion | Fix immediately |
|
||||
|
||||
### Rating Factors
|
||||
|
||||
Consider all three:
|
||||
|
||||
1. **Frequency:** How often does it occur?
|
||||
2. **Impact:** How severe when it occurs?
|
||||
3. **Persistence:** One-time or ongoing problem?
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
| Mistake | Why It Fails | Fix |
|
||||
|---------|-------------|------|
|
||||
| **Mystery meat navigation** | Icons without labels force guessing | Add text labels alongside icons |
|
||||
| **Too many choices** | Decision paralysis slows users | Reduce to 7 plus/minus 2 items |
|
||||
| **No "you are here" indicator** | Users feel lost in the hierarchy | Highlight current section in nav and breadcrumbs |
|
||||
| **No inline validation** | Submit, error, scroll cycle frustrates | Validate on blur with specific messages |
|
||||
| **Unclear required fields** | Users confused about what's mandatory | Mark optional fields, not required (most fields should be required) |
|
||||
| **Wall of text** | Nobody reads dense paragraphs | Break up with headings, bullets, whitespace |
|
||||
| **Jargon in labels** | Users don't speak your internal language | User-test all labels, use plain language |
|
||||
| **No loading indicators** | Users think the system is broken | Show spinner, progress bar, or skeleton screen |
|
||||
| **Tiny tap targets** | Mobile users misclick constantly | Minimum 44x44 px touch targets |
|
||||
| **Hover-only information** | Mobile and keyboard users miss it entirely | Don't hide critical info behind hover states |
|
||||
| **No undo** | Users afraid to take any action | Provide undo for all non-destructive actions |
|
||||
| **Poor error messages** | "Invalid input" tells users nothing | Explain what's wrong and how to fix it |
|
||||
| **Low contrast text** | Unreadable for many users | WCAG AA minimum (4.5:1 contrast ratio) |
|
||||
| **Inconsistent nav location** | Users can't find navigation | Fixed position, same location on every page |
|
||||
| **Broken back button** | Fundamental browser contract violated | Never hijack or break browser history |
|
||||
|
||||
## Quick Diagnostic
|
||||
|
||||
Audit any interface:
|
||||
|
||||
| Question | If No | Action |
|
||||
|----------|-------|--------|
|
||||
| Can I tell what site/page this is immediately? | Users are lost and disoriented | Add clear logo, page title, and breadcrumbs |
|
||||
| Is the main action obvious? | Users don't know what to do | Create visual hierarchy, single primary CTA |
|
||||
| Is the navigation clear? | Users can't find their way | Apply the Trunk Test, add "you are here" indicators |
|
||||
| Can I find the search? | Users with specific goals are blocked | Add visible search box in header |
|
||||
| Does the system show me what's happening? | Users lose trust and re-click | Add loading states, confirmations, progress indicators |
|
||||
| Are error messages helpful? | Users get stuck on errors | Rewrite in plain language with specific fix |
|
||||
| Can users undo or go back? | Users are afraid to act | Add undo, cancel, and back options everywhere |
|
||||
| Does it work without hover? | Mobile and keyboard users are excluded | Replace hover-only interactions with visible alternatives |
|
||||
| Are all interactive elements labeled? | Users guess at icon meanings | Add text labels or descriptive tooltips |
|
||||
| Does anything make me stop and think "huh?" | Cognitive load is too high | Simplify -- if it needs explanation, redesign it |
|
||||
|
||||
## Heuristic Conflicts
|
||||
|
||||
Heuristics sometimes contradict each other. When they do:
|
||||
- **Simplicity vs. Flexibility**: Use progressive disclosure
|
||||
- **Consistency vs. Context**: Consistent patterns, contextual prominence
|
||||
- **Efficiency vs. Error Prevention**: Prefer undo over confirmation dialogs
|
||||
- **Discoverability vs. Minimalism**: Primary actions visible, secondary hidden
|
||||
|
||||
See: [references/heuristic-conflicts.md](references/heuristic-conflicts.md) for resolution frameworks.
|
||||
|
||||
## Dark Patterns Recognition
|
||||
|
||||
Dark patterns violate heuristics deliberately to manipulate users:
|
||||
- Forced continuity (hard to cancel)
|
||||
- Roach motel (easy in, hard out)
|
||||
- Confirmshaming (guilt-based options)
|
||||
- Hidden costs (surprise fees at checkout)
|
||||
|
||||
See: [references/dark-patterns.md](references/dark-patterns.md) for complete taxonomy and ethical alternatives.
|
||||
|
||||
## When to Use Each Method
|
||||
|
||||
| Method | When | Time | Findings |
|
||||
|--------|------|------|----------|
|
||||
| Heuristic evaluation | Before user testing | 1-2 hours | Major violations |
|
||||
| User testing | After heuristic fixes | 2-4 hours | Real behavior |
|
||||
| A/B testing | When optimizing | Days-weeks | Statistical validation |
|
||||
| Analytics review | Ongoing | 30 min | Patterns and problems |
|
||||
|
||||
## Reference Files
|
||||
|
||||
- [krug-principles.md](references/krug-principles.md): Full Krug methodology, scanning behavior, navigation clarity
|
||||
- [nielsen-heuristics.md](references/nielsen-heuristics.md): Detailed heuristic explanations with examples
|
||||
- [audit-template.md](references/audit-template.md): Structured heuristic evaluation template
|
||||
- [dark-patterns.md](references/dark-patterns.md): Categories, examples, ethical alternatives, regulations
|
||||
- [wcag-checklist.md](references/wcag-checklist.md): Complete WCAG 2.1 AA checklist, testing tools
|
||||
- [cultural-ux.md](references/cultural-ux.md): RTL, color meanings, form conventions, localization
|
||||
- [heuristic-conflicts.md](references/heuristic-conflicts.md): When heuristics contradict, resolution frameworks
|
||||
|
||||
## Further Reading
|
||||
|
||||
This skill is based on usability principles developed by Steve Krug and Jakob Nielsen:
|
||||
|
||||
- [*"Don't Make Me Think, Revisited"*](https://www.amazon.com/Dont-Make-Think-Revisited-Usability/dp/0321965515?tag=wondelai00-20) by Steve Krug
|
||||
- [*"Rocket Surgery Made Easy"*](https://www.amazon.com/Rocket-Surgery-Made-Easy-Yourself/dp/0321657292?tag=wondelai00-20) by Steve Krug (DIY usability testing)
|
||||
- [*"10 Usability Heuristics for User Interface Design"*](https://www.nngroup.com/articles/ten-usability-heuristics/) by Jakob Nielsen (Nielsen Norman Group)
|
||||
|
||||
## About the Author
|
||||
|
||||
**Steve Krug** is a usability consultant who has been helping companies make their products more intuitive since the 1990s. His book *"Don't Make Me Think"* (first published in 2000, revised 2014) is the most widely read book on web usability and is considered essential reading for anyone involved in designing interfaces. Known for his accessible, humorous writing style and his advocacy for low-cost usability testing, Krug demonstrated that usability doesn't require a lab or a large budget -- just watching a few real users try to accomplish tasks.
|
||||
|
||||
**Jakob Nielsen, PhD** is co-founder of the Nielsen Norman Group (NN/g) and is widely regarded as the "king of usability." His 10 Usability Heuristics for User Interface Design, published in 1994, remain the most-used framework for heuristic evaluation worldwide. Nielsen has been called "the guru of Web page usability" by *The New York Times* and has authored numerous influential books on usability engineering. His research-driven approach to interface design helped establish usability as a recognized discipline in software development.
|
||||
226
skills/commissioning-skill/hook-patterns.md
Normal file
226
skills/commissioning-skill/hook-patterns.md
Normal file
@@ -0,0 +1,226 @@
|
||||
# Hook patterns
|
||||
|
||||
Hooks are compiled from spore decisions where the policy is fully deterministic.
|
||||
They fire via the cchooks runtime before the scion reasons, at zero token cost.
|
||||
|
||||
Install cchooks: `pip install cchooks` or `uv add cchooks`
|
||||
|
||||
---
|
||||
|
||||
## When to generate a hook vs. keep as spore-only
|
||||
|
||||
Generate a hook when:
|
||||
- The trigger condition is pattern-matchable without contextual judgment
|
||||
- The outcome is always the same given the trigger (no "it depends")
|
||||
- The decision can be encoded in ~20 lines of Python
|
||||
|
||||
Keep as spore-only when:
|
||||
- The decision requires the scion to interpret context before acting
|
||||
- The outcome varies based on factors not visible in the tool call metadata
|
||||
- The policy involves weighing tradeoffs
|
||||
|
||||
A spore entry should always exist for every hook (provenance). Not every spore
|
||||
entry needs a hook (judgment).
|
||||
|
||||
---
|
||||
|
||||
## File write guard
|
||||
|
||||
The most common hook type for annotator and transformer task classes. Blocks
|
||||
writes to sensitive paths before the scion acts.
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
# hooks/env-guard.py
|
||||
# Spore: env-file-write-guard
|
||||
# Blocks writes to credential and sensitive config files.
|
||||
|
||||
from cchooks import create_context, PreToolUseContext
|
||||
|
||||
SENSITIVE_PATTERNS = {".env", "secrets.json", "id_rsa", ".pem", ".key"}
|
||||
|
||||
c = create_context()
|
||||
assert isinstance(c, PreToolUseContext)
|
||||
|
||||
if c.tool_name == "Write":
|
||||
file_path = c.tool_input.get("file_path", "")
|
||||
if any(pattern in file_path for pattern in SENSITIVE_PATTERNS):
|
||||
c.output.deny(
|
||||
reason=f"Credential file protected: {file_path}",
|
||||
system_message="Sensitive file write blocked by spore policy. Escalate to parent if this write is intentional."
|
||||
)
|
||||
else:
|
||||
c.output.allow()
|
||||
else:
|
||||
c.output.allow()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Bash command guard
|
||||
|
||||
Blocks destructive or elevated-privilege bash patterns. Appropriate for any
|
||||
task class that does not require shell execution.
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
# hooks/bash-guard.py
|
||||
# Spore: destructive-bash-guard
|
||||
# Blocks bash commands matching destructive or privilege-escalation patterns.
|
||||
|
||||
from cchooks import create_context, PreToolUseContext
|
||||
|
||||
BLOCKED_PATTERNS = ["rm -rf", "sudo", "fdisk", "format", "dd if=", "mkfs"]
|
||||
|
||||
c = create_context()
|
||||
assert isinstance(c, PreToolUseContext)
|
||||
|
||||
if c.tool_name == "Bash":
|
||||
command = c.tool_input.get("command", "")
|
||||
for pattern in BLOCKED_PATTERNS:
|
||||
if pattern in command:
|
||||
c.output.deny(
|
||||
reason=f"Blocked pattern in bash command: {pattern}",
|
||||
system_message="Destructive command blocked by spore policy. Escalate to parent if this command is required."
|
||||
)
|
||||
break
|
||||
else:
|
||||
c.output.allow()
|
||||
else:
|
||||
c.output.allow()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Output format enforcer
|
||||
|
||||
Fires after a write and validates that output conforms to the required format.
|
||||
Appropriate for annotator task classes with strict output contracts.
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
# hooks/output-format-validator.py
|
||||
# Spore: annotation-output-format
|
||||
# Validates that annotation output files are valid JSONL.
|
||||
|
||||
import json
|
||||
from cchooks import create_context, PostToolUseContext
|
||||
|
||||
c = create_context()
|
||||
assert isinstance(c, PostToolUseContext)
|
||||
|
||||
if c.tool_name == "Write" and c.tool_input.get("file_path", "").endswith(".annotation"):
|
||||
content = c.tool_input.get("content", "")
|
||||
lines = [l.strip() for l in content.strip().splitlines() if l.strip()]
|
||||
invalid_lines = []
|
||||
for i, line in enumerate(lines):
|
||||
try:
|
||||
json.loads(line)
|
||||
except json.JSONDecodeError:
|
||||
invalid_lines.append(i + 1)
|
||||
if invalid_lines:
|
||||
c.output.challenge(
|
||||
reason=f"Output format violation: lines {invalid_lines} are not valid JSON",
|
||||
system_message="Annotation output must be JSONL (one JSON object per line). Correct and retry."
|
||||
)
|
||||
else:
|
||||
c.output.accept()
|
||||
else:
|
||||
c.output.accept()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Session start context loader
|
||||
|
||||
Fires when the scion session starts and loads the spore file path into context.
|
||||
Use this to ensure the scion always reads its spore file on startup.
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
# hooks/session-start.py
|
||||
# Loads spore file contents into session context on startup.
|
||||
|
||||
import os
|
||||
from cchooks import create_context, SessionStartContext
|
||||
|
||||
SPORE_FILE = os.environ.get("SCION_SPORE_FILE", "")
|
||||
|
||||
c = create_context()
|
||||
assert isinstance(c, SessionStartContext)
|
||||
|
||||
if c.source == "startup" and SPORE_FILE and os.path.exists(SPORE_FILE):
|
||||
with open(SPORE_FILE) as f:
|
||||
content = f.read()
|
||||
print(f"Spore policy loaded from {SPORE_FILE}:\n\n{content}")
|
||||
|
||||
c.output.exit_success()
|
||||
```
|
||||
|
||||
Set `SCION_SPORE_FILE` in the scion's environment at spawn time. The commissioning
|
||||
skill is responsible for setting this variable to the correct spore file path.
|
||||
|
||||
---
|
||||
|
||||
## Hook registration in claude settings
|
||||
|
||||
Register hooks in `.claude/settings.json` at the project or user level:
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "Write",
|
||||
"hooks": [
|
||||
{ "type": "command", "command": "python hooks/env-guard.py" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": "Bash",
|
||||
"hooks": [
|
||||
{ "type": "command", "command": "python hooks/bash-guard.py" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Write",
|
||||
"hooks": [
|
||||
{ "type": "command", "command": "python hooks/output-format-validator.py" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"SessionStart": [
|
||||
{
|
||||
"hooks": [
|
||||
{ "type": "command", "command": "python hooks/session-start.py" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Validation before registration
|
||||
|
||||
Every hook must be validated before the commissioning skill registers it.
|
||||
Minimal validation: run the hook with a synthetic input that should trigger
|
||||
the deny/challenge condition and confirm it fires correctly.
|
||||
|
||||
```bash
|
||||
# Test env-guard with a synthetic Write event targeting .env
|
||||
echo '{"tool_name": "Write", "tool_input": {"file_path": ".env", "content": "SECRET=x"}}' \
|
||||
| python hooks/env-guard.py
|
||||
# Expected: exit code 2 (deny) with reason message
|
||||
|
||||
# Test env-guard with a safe path — should allow
|
||||
echo '{"tool_name": "Write", "tool_input": {"file_path": "output.txt", "content": "hello"}}' \
|
||||
| python hooks/env-guard.py
|
||||
# Expected: exit code 0 (allow)
|
||||
```
|
||||
|
||||
If a hook does not pass both cases, do not register it and do not reference it
|
||||
in the spore entry. Fix or remove before depositing.
|
||||
366
skills/commissioning-skill/hook-protocol.md
Normal file
366
skills/commissioning-skill/hook-protocol.md
Normal file
@@ -0,0 +1,366 @@
|
||||
# Hook protocol
|
||||
|
||||
Claude Code hooks are executables that intercept agent lifecycle events. They
|
||||
require no SDK or external library — the protocol is just stdin JSON in,
|
||||
exit code out. This file contains everything needed to generate correct hooks
|
||||
in Python (stdlib only) or JavaScript (Node.js, no packages).
|
||||
|
||||
---
|
||||
|
||||
## The protocol
|
||||
|
||||
Claude Code spawns the hook executable, writes a JSON event to its stdin, and
|
||||
reads the exit code and stdout when it exits.
|
||||
|
||||
**Exit codes:**
|
||||
|
||||
- `0` — allow / success. Claude Code continues. Any stdout is added to context.
|
||||
- `1` — non-blocking error. Claude Code sees the message and continues anyway.
|
||||
- `2` — blocking deny. Claude Code stops the action and shows the reason.
|
||||
|
||||
**JSON control output** (optional, write to stdout as valid JSON):
|
||||
|
||||
For `PreToolUse` hooks, you can output a JSON object instead of relying solely
|
||||
on the exit code. This gives you a reason string that Claude Code surfaces to
|
||||
the agent:
|
||||
|
||||
```json
|
||||
{"decision": "block", "reason": "Credential file write blocked by spore policy"}
|
||||
```
|
||||
|
||||
```json
|
||||
{"decision": "approve"}
|
||||
```
|
||||
|
||||
For `PostToolUse` hooks, stdout text (not JSON) is injected into the agent's
|
||||
context as tool result feedback.
|
||||
|
||||
**Input event shape** (arrives on stdin as a single JSON line):
|
||||
|
||||
```json
|
||||
{
|
||||
"tool_name": "Write",
|
||||
"tool_input": {
|
||||
"file_path": "path/to/file",
|
||||
"content": "file content"
|
||||
},
|
||||
"tool_response": "...",
|
||||
"session_id": "abc123"
|
||||
}
|
||||
```
|
||||
|
||||
`tool_response` is only present on `PostToolUse` events.
|
||||
`tool_input` shape varies by tool — see field names below.
|
||||
|
||||
**Common tool_input fields by tool:**
|
||||
|
||||
- `Write` / `Edit` / `MultiEdit`: `file_path`, `content`
|
||||
- `Bash`: `command`
|
||||
- `Read`: `file_path`
|
||||
- `WebFetch`: `url`
|
||||
|
||||
---
|
||||
|
||||
## Python template (stdlib only)
|
||||
|
||||
Use this for projects that have Python available. Zero dependencies — no pip
|
||||
install, no virtualenv. Copy, rename, fill in the guard logic.
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
# hooks/{hook-name}.py
|
||||
# Spore: {spore-identifier}
|
||||
# {one-line description of what this hook guards}
|
||||
#
|
||||
# Protocol: reads JSON event from stdin, exits 0 (allow) or 2 (deny).
|
||||
# No external dependencies — stdlib only.
|
||||
|
||||
import json
|
||||
import sys
|
||||
|
||||
|
||||
def main() -> None:
|
||||
# Read the full event from stdin
|
||||
try:
|
||||
event = json.loads(sys.stdin.read())
|
||||
except (json.JSONDecodeError, OSError):
|
||||
# If we can't read the event, allow and let the agent handle it
|
||||
sys.exit(0)
|
||||
|
||||
tool_name = event.get("tool_name", "")
|
||||
tool_input = event.get("tool_input", {})
|
||||
|
||||
# --- Guard logic starts here ---
|
||||
# Replace this block with the compiled spore decision.
|
||||
# Example: block writes to credential files.
|
||||
if tool_name == "Write":
|
||||
file_path = tool_input.get("file_path", "")
|
||||
BLOCKED = {".env", "secrets.json", "id_rsa", ".pem", ".key"}
|
||||
if any(pattern in file_path for pattern in BLOCKED):
|
||||
# Write structured denial to stdout for Claude Code to surface
|
||||
print(json.dumps({
|
||||
"decision": "block",
|
||||
"reason": f"Credential file protected by spore policy: {file_path}"
|
||||
}))
|
||||
sys.exit(2)
|
||||
# --- Guard logic ends here ---
|
||||
|
||||
# Default: allow
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## JavaScript template (Node.js, no packages)
|
||||
|
||||
Use this for TypeScript/JavaScript projects, or any project where Node.js is
|
||||
available (Claude Code itself runs on Node so it is always present). Zero
|
||||
dependencies — pure Node.js stdlib.
|
||||
|
||||
```javascript
|
||||
#!/usr/bin/env node
|
||||
// hooks/{hook-name}.js
|
||||
// Spore: {spore-identifier}
|
||||
// {one-line description of what this hook guards}
|
||||
//
|
||||
// Protocol: reads JSON event from stdin, exits 0 (allow) or 2 (deny).
|
||||
// No external dependencies — Node.js stdlib only.
|
||||
|
||||
async function main() {
|
||||
// Read the full event from stdin
|
||||
let raw = "";
|
||||
for await (const chunk of process.stdin) {
|
||||
raw += chunk;
|
||||
}
|
||||
|
||||
let event;
|
||||
try {
|
||||
event = JSON.parse(raw);
|
||||
} catch {
|
||||
// If we can't parse the event, allow and let the agent handle it
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const toolName = event.tool_name ?? "";
|
||||
const toolInput = event.tool_input ?? {};
|
||||
|
||||
// --- Guard logic starts here ---
|
||||
// Replace this block with the compiled spore decision.
|
||||
// Example: block writes to credential files.
|
||||
if (toolName === "Write") {
|
||||
const filePath = toolInput.file_path ?? "";
|
||||
const blocked = [".env", "secrets.json", "id_rsa", ".pem", ".key"];
|
||||
if (blocked.some((pattern) => filePath.includes(pattern))) {
|
||||
// Write structured denial to stdout for Claude Code to surface
|
||||
process.stdout.write(JSON.stringify({
|
||||
decision: "block",
|
||||
reason: `Credential file protected by spore policy: ${filePath}`,
|
||||
}));
|
||||
process.exit(2);
|
||||
}
|
||||
}
|
||||
// --- Guard logic ends here ---
|
||||
|
||||
// Default: allow
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
main().catch(() => process.exit(0)); // On unexpected error, fail open
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## PostToolUse validator template (Python)
|
||||
|
||||
PostToolUse hooks receive the completed tool result and can inject feedback
|
||||
into the agent's context. Use for output format validation, quality checks,
|
||||
or logging.
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
# hooks/{hook-name}-post.py
|
||||
# Spore: {spore-identifier}
|
||||
# Validates output after a tool completes. Injects feedback into agent context.
|
||||
|
||||
import json
|
||||
import sys
|
||||
|
||||
|
||||
def main() -> None:
|
||||
try:
|
||||
event = json.loads(sys.stdin.read())
|
||||
except (json.JSONDecodeError, OSError):
|
||||
sys.exit(0)
|
||||
|
||||
tool_name = event.get("tool_name", "")
|
||||
tool_input = event.get("tool_input", {})
|
||||
# tool_response contains what the tool returned
|
||||
tool_response = event.get("tool_response", "")
|
||||
|
||||
# --- Validation logic starts here ---
|
||||
# Example: verify annotation output is valid JSONL
|
||||
if tool_name == "Write":
|
||||
file_path = tool_input.get("file_path", "")
|
||||
if file_path.endswith(".annotation"):
|
||||
content = tool_input.get("content", "")
|
||||
invalid = []
|
||||
for i, line in enumerate(content.strip().splitlines(), 1):
|
||||
if line.strip():
|
||||
try:
|
||||
json.loads(line)
|
||||
except json.JSONDecodeError:
|
||||
invalid.append(i)
|
||||
if invalid:
|
||||
# Exit 2 to block; message shown to agent
|
||||
print(json.dumps({
|
||||
"decision": "block",
|
||||
"reason": f"Output format violation: lines {invalid} are not valid JSON. "
|
||||
f"Annotation output must be JSONL (one JSON object per line)."
|
||||
}))
|
||||
sys.exit(2)
|
||||
# --- Validation logic ends here ---
|
||||
|
||||
# Exit 0; any stdout text gets injected into agent context as feedback
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## SessionStart context loader template (Python)
|
||||
|
||||
Loads the spore file into session context on startup so the scion has its
|
||||
policy available from the first turn.
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
# hooks/session-start.py
|
||||
# Loads SCION_SPORE_FILE contents into session context on startup.
|
||||
# The commissioning skill sets SCION_SPORE_FILE at spawn time.
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def main() -> None:
|
||||
try:
|
||||
event = json.loads(sys.stdin.read())
|
||||
except (json.JSONDecodeError, OSError):
|
||||
sys.exit(0)
|
||||
|
||||
# Only act on startup, not resume or clear
|
||||
if event.get("source") != "startup":
|
||||
sys.exit(0)
|
||||
|
||||
spore_file = os.environ.get("SCION_SPORE_FILE", "")
|
||||
if not spore_file or not os.path.exists(spore_file):
|
||||
# No spore file configured — allow and continue
|
||||
sys.exit(0)
|
||||
|
||||
try:
|
||||
with open(spore_file) as f:
|
||||
content = f.read()
|
||||
# Print to stdout — SessionStart hook output is added to session context
|
||||
print(f"Spore policy active ({spore_file}):\n\n{content}")
|
||||
except OSError:
|
||||
pass # Fail open — don't block startup if spore file unreadable
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Registration in .claude/settings.json
|
||||
|
||||
Register hooks at the module level (`.claude/settings.json` in the module
|
||||
folder) rather than globally. This keeps hook scope aligned with spore scope.
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "Write",
|
||||
"hooks": [
|
||||
{ "type": "command", "command": "python hooks/env-guard.py" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": "Bash",
|
||||
"hooks": [
|
||||
{ "type": "command", "command": "python hooks/bash-guard.py" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Write",
|
||||
"hooks": [
|
||||
{ "type": "command", "command": "python hooks/output-validator-post.py" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"SessionStart": [
|
||||
{
|
||||
"hooks": [
|
||||
{ "type": "command", "command": "python hooks/session-start.py" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For Node.js hooks, replace `python hooks/name.py` with `node hooks/name.js`.
|
||||
For executable hooks (chmod +x), the command can be the file path directly.
|
||||
|
||||
---
|
||||
|
||||
## Choosing Python vs JavaScript
|
||||
|
||||
Use Python when the project uses Python or when no preference is stated —
|
||||
Python stdlib is universally available and the syntax reads clearly to most
|
||||
agents. Use JavaScript (Node.js) when the project is a TypeScript/JavaScript
|
||||
codebase, or when the hook needs to share logic with existing project tooling.
|
||||
The bartolli pattern (`.claude/hooks/{project-type}/quality-check.js`) is
|
||||
worth adopting when multiple hook types share configuration — see that repo
|
||||
for a mature example of project-type-aware Node.js hooks with SHA256 config
|
||||
caching for performance.
|
||||
|
||||
Either language produces identical protocol behavior. The hook protocol does
|
||||
not care what generates the exit code.
|
||||
|
||||
---
|
||||
|
||||
## Validation before registration
|
||||
|
||||
Every generated hook must pass two test cases before the commissioning skill
|
||||
registers it: one that should trigger the deny condition, and one that should
|
||||
allow cleanly. Run manually:
|
||||
|
||||
```bash
|
||||
# Should exit 2 (deny) — triggers the guard
|
||||
echo '{"tool_name":"Write","tool_input":{"file_path":".env","content":"SECRET=x"}}' \
|
||||
| python hooks/env-guard.py
|
||||
echo "Exit code: $?"
|
||||
|
||||
# Should exit 0 (allow) — safe path
|
||||
echo '{"tool_name":"Write","tool_input":{"file_path":"output.txt","content":"hello"}}' \
|
||||
| python hooks/env-guard.py
|
||||
echo "Exit code: $?"
|
||||
```
|
||||
|
||||
If either test fails, do not register the hook or reference it in a spore entry.
|
||||
Fix first, validate again, then deposit.
|
||||
163
skills/commissioning-skill/seeds-entry-format.md
Normal file
163
skills/commissioning-skill/seeds-entry-format.md
Normal file
@@ -0,0 +1,163 @@
|
||||
# SEEDS.md entry format
|
||||
|
||||
SEEDS.md lives at the module root and serves two purposes simultaneously:
|
||||
a routing index for the commissioning skill, and a provenance log for the
|
||||
parent agent. Each entry corresponds to one spore file.
|
||||
|
||||
---
|
||||
|
||||
## File header
|
||||
|
||||
```markdown
|
||||
# SEEDS.md
|
||||
# module: {module-name}
|
||||
# maintained-by: parent agent
|
||||
# last-updated: {YYYY-MM-DD}
|
||||
|
||||
---
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Entry schema
|
||||
|
||||
```yaml
|
||||
---
|
||||
task-class: {task-class-name}
|
||||
spore-file: spores/{task-class}-spores.md
|
||||
status: active | provisional | superseded
|
||||
deposited: {YYYY-MM-DD}
|
||||
supersedes: {path-to-old-spore-file} | null
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
[1-3 sentences. What situation generated this decision class. What the module
|
||||
was doing when this task class was first encountered. Why a spore file was
|
||||
needed rather than relying on the scion's base reasoning.]
|
||||
|
||||
## Reasoning
|
||||
|
||||
[The parent's full reasoning. Why each policy in the spore file was chosen.
|
||||
What edge cases were considered. What alternatives were rejected and why.
|
||||
Write for a frontier model reading this months later with no conversation
|
||||
context. This section has no token constraint — optimize for completeness.]
|
||||
|
||||
## Revision triggers
|
||||
|
||||
[Bullet list of conditions that would make this entry stale or require
|
||||
a new spore file to be composed:]
|
||||
- {Condition 1}
|
||||
- {Condition 2}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Status definitions
|
||||
|
||||
`active` — the spore file has been used by at least one scion run without
|
||||
unexpected escalation on covered decisions. Treat as current.
|
||||
|
||||
`provisional` — newly composed, not yet validated in production. The commissioning
|
||||
skill should flag after first scion run. Promote to `active` if the scion
|
||||
completed its task without escalating on covered decisions. If unexpected
|
||||
escalations occurred, update the spore file and seed entry before promoting.
|
||||
|
||||
`superseded` — this entry has been replaced by a newer one. The `supersedes`
|
||||
field in the newer entry points to the old spore file path. Retain superseded
|
||||
entries for audit — do not delete. Skip during routing.
|
||||
|
||||
---
|
||||
|
||||
## Routing rules for the commissioning skill
|
||||
|
||||
The commissioning skill scans frontmatter only on first pass. The scan should:
|
||||
|
||||
1. Filter by `task-class` matching the current task
|
||||
2. Filter by `status` not equal to `superseded`
|
||||
3. If a match is found, read `spore-file` to get the path
|
||||
4. Read the entry body only if the parent needs to review or revise the policy
|
||||
|
||||
The body is for deliberate review, not for automated routing. Do not load
|
||||
entry bodies into the routing scan — they are for the parent, not the pipeline.
|
||||
|
||||
---
|
||||
|
||||
## Full SEEDS.md example
|
||||
|
||||
```markdown
|
||||
# SEEDS.md
|
||||
# module: data-pipeline
|
||||
# maintained-by: parent agent
|
||||
# last-updated: 2026-03-20
|
||||
|
||||
---
|
||||
task-class: annotator
|
||||
spore-file: spores/annotator-spores.md
|
||||
status: active
|
||||
deposited: 2026-03-20
|
||||
supersedes: null
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
The data-pipeline module processes raw input files and produces annotated JSONL
|
||||
output. Annotator scions handle the core transformation. The first annotator
|
||||
scion run encountered a write attempt to a .env file and an ambiguous output
|
||||
format question — both were resolved and crystallized as spore entries.
|
||||
|
||||
## Reasoning
|
||||
|
||||
Credential file protection: the annotator task class has no legitimate reason
|
||||
to write to .env or secrets files. The decision to deny unconditionally (rather
|
||||
than escalate) was made because even a legitimate-seeming reason from a Haiku-class
|
||||
scion is not sufficient justification for credential file access — escalate for
|
||||
that. A hook was generated because the condition is fully deterministic.
|
||||
|
||||
Output format: JSONL was chosen over JSON array because annotator runs are
|
||||
long-running and JSONL allows streaming consumption by downstream validators
|
||||
without waiting for run completion. This is a module convention, not a general
|
||||
principle — other modules may differ.
|
||||
|
||||
Bash guard: the annotator task class requires read and write access but never
|
||||
needs to execute shell commands. The deny-all-bash policy was chosen over a
|
||||
more permissive pattern because any bash requirement from an annotator scion
|
||||
is unexpected and warrants parent review.
|
||||
|
||||
## Revision triggers
|
||||
|
||||
- If the module adds a legitimate use case for bash execution in annotation
|
||||
- If the output format changes from JSONL to another format
|
||||
- If credential file patterns change (new file types added to the project)
|
||||
- If annotator task class is split into sub-classes with different permission needs
|
||||
|
||||
---
|
||||
task-class: validator
|
||||
spore-file: spores/validator-spores.md
|
||||
status: provisional
|
||||
deposited: 2026-03-20
|
||||
supersedes: null
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
Validator scions check annotator output against a schema. First commissioned
|
||||
2026-03-20. Not yet run in production — status provisional pending first run.
|
||||
|
||||
## Reasoning
|
||||
|
||||
Read-only access: validators should not write to source files. The deny-write
|
||||
policy covers all paths except the validator's own output directory. This is
|
||||
conservative — if a legitimate write need emerges, the scion will escalate and
|
||||
the policy can be revised.
|
||||
|
||||
Schema mismatch escalation: rather than having the validator silently fail or
|
||||
produce empty output on schema mismatch, escalation was chosen so the parent
|
||||
can decide whether the schema has drifted or the annotation has an error.
|
||||
|
||||
## Revision triggers
|
||||
|
||||
- After first production run — review escalation rate; if too high, the
|
||||
schema-mismatch trigger may be too sensitive
|
||||
- If validator gains write permissions to a specific output path
|
||||
```
|
||||
90
skills/commissioning-skill/skill-scout.md
Normal file
90
skills/commissioning-skill/skill-scout.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# skill-scout — commissioning skill extension
|
||||
# Fetches purpose-specific external skills at commission time.
|
||||
# Add this capability to the commissioning skill Step 3 (compose path)
|
||||
# whenever a builder scion is being commissioned.
|
||||
|
||||
---
|
||||
|
||||
## When to run skill-scout
|
||||
|
||||
Run skill-scout before commissioning any builder scion. The sorter scion
|
||||
and commit-tracker hook do not require external skills — they are covered
|
||||
by their spore files alone. The builder scion does, because the quality
|
||||
of its output depends on the aesthetic and technical conventions of the
|
||||
build type (frontend-design, annotated-writing, etc.).
|
||||
|
||||
---
|
||||
|
||||
## Skill-scout decision table
|
||||
|
||||
| Section being built | Recommended external skill | Source |
|
||||
|------------------------|--------------------------------------|---------------------------------|
|
||||
| Home page (star-map) | frontend-design + ux-design | anthropics/skills + wondelai |
|
||||
| Writings | annotated-writing | project skills (user-supplied) |
|
||||
| Videos | frontend-design | anthropics/skills |
|
||||
| Images (slideshow) | frontend-design | anthropics/skills |
|
||||
| Playlists / Watchlists | frontend-design | anthropics/skills |
|
||||
| Music gallery | frontend-design | anthropics/skills |
|
||||
| Creature Playground | commissioning-skill (recursive) | this workspace |
|
||||
| Any section (UI/UX) | ux-design (refactoring-ui + ux-heuristics) | wondelai/skills |
|
||||
|
||||
---
|
||||
|
||||
## Skill-scout process
|
||||
|
||||
1. Identify the section being built from the ToBuild/{section}/ path
|
||||
2. Look up the recommended skill from the decision table above
|
||||
3. Check whether the skill is already present in commissioning-skill/fetched-skills/
|
||||
4. If absent: fetch from source
|
||||
- For GitHub-hosted skills: clone or download the SKILL.md (and references/ if present)
|
||||
- Save to commissioning-skill/fetched-skills/{skill-name}/
|
||||
5. Set SCION_EXTERNAL_SKILL env var to the fetched skill path before spawning builder
|
||||
6. Include fetched skill contents in the builder scion's commission package
|
||||
|
||||
## Fetched skill cache
|
||||
|
||||
```
|
||||
commissioning-skill/
|
||||
└── fetched-skills/
|
||||
├── frontend-design/
|
||||
│ └── SKILL.md
|
||||
├── annotated-writing/
|
||||
│ ├── SKILL.md
|
||||
│ └── references/
|
||||
└── ux-design/
|
||||
└── SKILL.md
|
||||
```
|
||||
|
||||
Fetched skills are cached between commission cycles. Do not re-fetch on
|
||||
every commission — check the cache first. Re-fetch only if the skill file
|
||||
is older than 7 days or if the parent explicitly requests a refresh.
|
||||
|
||||
---
|
||||
|
||||
## Skill-scout for Creature Playground
|
||||
|
||||
The Creature Playground section is special: scions build there autonomously.
|
||||
The parent commissions a builder scion with the commissioning-skill itself
|
||||
bundled (recursive commissioning). The scion can then commission sub-scions
|
||||
of its own within the Playground scope. Output is for review only — nothing
|
||||
in Creature Playground is deployed to the live site without explicit parent
|
||||
review and JL's commit trigger.
|
||||
|
||||
---
|
||||
|
||||
## Integration point in commissioning skill
|
||||
|
||||
Add between Step 3b (reason through decision surface) and Step 3c (write
|
||||
spore entries) for builder task class:
|
||||
|
||||
```
|
||||
3b-scout: If task-class is builder:
|
||||
- Identify section from ToBuild path
|
||||
- Run skill-scout to fetch appropriate external skill
|
||||
- Set SCION_EXTERNAL_SKILL to fetched skill path
|
||||
- Proceed to 3c with external skill context available
|
||||
```
|
||||
|
||||
The external skill content should be appended to the builder scion's spore
|
||||
file reference list, not embedded inline — keep the spore file itself within
|
||||
its token budget.
|
||||
170
skills/commissioning-skill/spore-entry-format.md
Normal file
170
skills/commissioning-skill/spore-entry-format.md
Normal file
@@ -0,0 +1,170 @@
|
||||
# Spore entry format
|
||||
|
||||
Each entry in a `*-spores.md` file is a self-contained, atomic policy record.
|
||||
One trigger condition. One outcome. No prose reasoning.
|
||||
|
||||
---
|
||||
|
||||
## Schema
|
||||
|
||||
```yaml
|
||||
---
|
||||
spore: {kebab-case-identifier}
|
||||
task-class: {task-class-name}
|
||||
scion-model: {model-identifier}
|
||||
deposited: {YYYY-MM-DD}
|
||||
hook: {relative-path-to-hook} | nil
|
||||
---
|
||||
policy: {single declarative sentence describing the decision}
|
||||
trigger: {event} · {condition}
|
||||
on-match: allow | deny | escalate
|
||||
on-novel: escalate to parent
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Field definitions
|
||||
|
||||
`spore` — unique identifier within the file. Kebab-case. Descriptive enough to
|
||||
identify the decision at a glance without reading the body.
|
||||
|
||||
`task-class` — the task class this entry was written for. Should match the
|
||||
filename stem (e.g. `annotator` in `annotator-spores.md`).
|
||||
|
||||
`scion-model` — the model this entry was token-budgeted for. Entries written for
|
||||
Haiku-4.5 are valid for Sonnet-class scions (more capable model, easier load)
|
||||
but not necessarily vice versa.
|
||||
|
||||
`deposited` — ISO date the entry was first written.
|
||||
|
||||
`hook` — relative path to the compiled cchooks Python file, if one exists for
|
||||
this decision. `nil` if the decision requires scion interpretation and has no
|
||||
compiled hook.
|
||||
|
||||
`policy` — one declarative sentence. What the scion should do. Not why. Not when.
|
||||
Just what. The trigger handles when.
|
||||
|
||||
`trigger` — the event and condition that activates this policy. Use the format:
|
||||
`{HookEvent} · {condition}`. Hook events are: `PreToolUse`, `PostToolUse`,
|
||||
`UserPromptSubmit`, `Stop`, `SessionStart`, `SessionEnd`.
|
||||
|
||||
`on-match` — the outcome when trigger fires: `allow`, `deny`, or `escalate`.
|
||||
|
||||
`on-novel` — always `escalate to parent`. This is the fallback for situations
|
||||
not covered by any spore entry.
|
||||
|
||||
---
|
||||
|
||||
## Token discipline
|
||||
|
||||
Each entry must stay under approximately 60 tokens. Count before writing.
|
||||
The policy line is the most likely offender — keep it to one clause.
|
||||
The trigger line should be terse: event type, one condition, nothing more.
|
||||
|
||||
Reasoning about *why* a policy exists belongs in the corresponding SEEDS.md
|
||||
entry body, not here. If you find yourself wanting to add a comment or
|
||||
explanation to a spore entry, that content goes in SEEDS.md instead.
|
||||
|
||||
---
|
||||
|
||||
## Examples
|
||||
|
||||
### File write guard (with hook)
|
||||
|
||||
```yaml
|
||||
---
|
||||
spore: env-file-write-guard
|
||||
task-class: annotator
|
||||
scion-model: haiku-4.5
|
||||
deposited: 2026-03-20
|
||||
hook: hooks/env-guard.py
|
||||
---
|
||||
policy: deny all writes to sensitive credential files
|
||||
trigger: PreToolUse · tool=Write · file_path matches *.env|secrets.json|id_rsa
|
||||
on-match: deny
|
||||
on-novel: escalate to parent
|
||||
```
|
||||
|
||||
### Output format enforcement (no hook)
|
||||
|
||||
```yaml
|
||||
---
|
||||
spore: annotation-output-format
|
||||
task-class: annotator
|
||||
scion-model: haiku-4.5
|
||||
deposited: 2026-03-20
|
||||
hook: nil
|
||||
---
|
||||
policy: write all annotation output as JSONL to stdout, one record per line
|
||||
trigger: PostToolUse · tool=Write · file_path matches *.annotation
|
||||
on-match: allow
|
||||
on-novel: escalate to parent
|
||||
```
|
||||
|
||||
### Bash safety guard (with hook)
|
||||
|
||||
```yaml
|
||||
---
|
||||
spore: destructive-bash-guard
|
||||
task-class: annotator
|
||||
scion-model: haiku-4.5
|
||||
deposited: 2026-03-20
|
||||
hook: hooks/bash-guard.py
|
||||
---
|
||||
policy: deny bash commands containing destructive patterns
|
||||
trigger: PreToolUse · tool=Bash · command matches rm -rf|sudo|fdisk|format
|
||||
on-match: deny
|
||||
on-novel: escalate to parent
|
||||
```
|
||||
|
||||
### Escalation trigger (no hook)
|
||||
|
||||
```yaml
|
||||
---
|
||||
spore: schema-mismatch-escalate
|
||||
task-class: annotator
|
||||
scion-model: haiku-4.5
|
||||
deposited: 2026-03-20
|
||||
hook: nil
|
||||
---
|
||||
policy: escalate if input schema does not match expected annotation schema
|
||||
trigger: PreToolUse · tool=Read · file_path matches *.input · schema-mismatch detected
|
||||
on-match: escalate
|
||||
on-novel: escalate to parent
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Full spore file example
|
||||
|
||||
```markdown
|
||||
# annotator-spores.md
|
||||
# task-class: annotator
|
||||
# scion-model: haiku-4.5
|
||||
# token-budget: 600
|
||||
# last-updated: 2026-03-20
|
||||
|
||||
---
|
||||
spore: env-file-write-guard
|
||||
task-class: annotator
|
||||
scion-model: haiku-4.5
|
||||
deposited: 2026-03-20
|
||||
hook: hooks/env-guard.py
|
||||
---
|
||||
policy: deny all writes to sensitive credential files
|
||||
trigger: PreToolUse · tool=Write · file_path matches *.env|secrets.json|id_rsa
|
||||
on-match: deny
|
||||
on-novel: escalate to parent
|
||||
|
||||
---
|
||||
spore: annotation-output-format
|
||||
task-class: annotator
|
||||
scion-model: haiku-4.5
|
||||
deposited: 2026-03-20
|
||||
hook: nil
|
||||
---
|
||||
policy: write annotation output as JSONL to stdout, one record per line
|
||||
trigger: PostToolUse · tool=Write · file_path matches *.annotation
|
||||
on-match: allow
|
||||
on-novel: escalate to parent
|
||||
```
|
||||
78
skills/commissioning-skill/token-budgets.md
Normal file
78
skills/commissioning-skill/token-budgets.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# Token budgets
|
||||
|
||||
Token budgets for spore files are set by the parent agent at commission time
|
||||
based on the scion's model class. The goal is not to minimise tokens — it is
|
||||
to maximise signal density within the model's reliable execution window.
|
||||
|
||||
---
|
||||
|
||||
## Why this matters
|
||||
|
||||
Context rot is empirically universal: every model degrades before its nominal
|
||||
context window limit, and smaller models degrade faster. A dense, denoised
|
||||
500-token spore file produces better scion outputs than a verbose 2,000-token
|
||||
one — not just cheaper outputs. Signal density is a quality constraint, not
|
||||
only a cost constraint.
|
||||
|
||||
Source: Chroma research 2025; Liu et al. "Lost in the Middle" 2024;
|
||||
CompLLM 2025 (compressed context outperforming uncompressed on small models).
|
||||
|
||||
The nominal context window is almost irrelevant. What matters is the model's
|
||||
reliable execution window — where multi-step reasoning remains coherent. For
|
||||
agentic tasks this is a fraction of the nominal limit.
|
||||
|
||||
---
|
||||
|
||||
## Per-model ceilings
|
||||
|
||||
These are working estimates. Not empirically tested against this architecture.
|
||||
Measure scion task success rate as a function of spore file size for your
|
||||
specific task classes, then adjust.
|
||||
|
||||
| Scion model class | Nominal window | Spore file ceiling | Per-entry target |
|
||||
|-----------------------|----------------|-------------------|-----------------|
|
||||
| Haiku 4.5 | 200K | 600 tokens | ~60 tokens |
|
||||
| Gemini 2.5 Flash | 1M | 800 tokens | ~60 tokens |
|
||||
| Sonnet-class scion | 200K | 1,200 tokens | ~80 tokens |
|
||||
|
||||
The ceiling applies to the full spore file content, not per entry.
|
||||
At 60 tokens per entry, a 600-token ceiling accommodates approximately
|
||||
8-10 entries — enough for most well-scoped task classes.
|
||||
|
||||
---
|
||||
|
||||
## Frontier model note
|
||||
|
||||
A frontier parent reading a spore file written for Haiku-class scions will
|
||||
process it with less interpretive load, not more. The density that is required
|
||||
for a small model is simply easier for a large model. There is no upper bound
|
||||
on how dense context can be for a frontier model — only a lower bound on how
|
||||
dense it must be for a small one.
|
||||
|
||||
This means spore files composed for the smallest expected scion are valid for
|
||||
any larger scion that might also read them. Write for the least capable consumer.
|
||||
|
||||
---
|
||||
|
||||
## Prompt caching note
|
||||
|
||||
Spore files are near-perfect prompt caching targets because they change slowly
|
||||
and load at the beginning of the scion's context. Anthropic's prompt caching
|
||||
charges 10% of base input token price on cache hits. A stable spore file
|
||||
achieves near-100% cache hit rate — the token cost is effectively paid once
|
||||
across all scion runs in its task class.
|
||||
|
||||
Keep spore file content stable between runs. Avoid injecting timestamps,
|
||||
run IDs, or other variable content into the spore file — variable content
|
||||
breaks the cache prefix and forces a full re-read on every run.
|
||||
|
||||
---
|
||||
|
||||
## Counting tokens
|
||||
|
||||
Rough heuristic for English prose: 1 token ≈ 0.75 words, or ~4 characters.
|
||||
A 60-token entry is approximately 45 words or 240 characters. Write an entry,
|
||||
count the words, multiply by 1.33 — if the result exceeds 60, trim.
|
||||
|
||||
For precise counts, use the Anthropic tokenizer or the `tiktoken` library
|
||||
(cl100k_base encoding as a close approximation for Claude models).
|
||||
946
skills/composition-patterns/AGENTS.md
Normal file
946
skills/composition-patterns/AGENTS.md
Normal file
@@ -0,0 +1,946 @@
|
||||
# React Composition Patterns
|
||||
|
||||
**Version 1.0.0**
|
||||
Engineering
|
||||
January 2026
|
||||
|
||||
> **Note:**
|
||||
> This document is mainly for agents and LLMs to follow when maintaining,
|
||||
> generating, or refactoring React codebases using composition. Humans
|
||||
> may also find it useful, but guidance here is optimized for automation
|
||||
> and consistency by AI-assisted workflows.
|
||||
|
||||
---
|
||||
|
||||
## Abstract
|
||||
|
||||
Composition patterns for building flexible, maintainable React components. Avoid boolean prop proliferation by using compound components, lifting state, and composing internals. These patterns make codebases easier for both humans and AI agents to work with as they scale.
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Component Architecture](#1-component-architecture) — **HIGH**
|
||||
- 1.1 [Avoid Boolean Prop Proliferation](#11-avoid-boolean-prop-proliferation)
|
||||
- 1.2 [Use Compound Components](#12-use-compound-components)
|
||||
2. [State Management](#2-state-management) — **MEDIUM**
|
||||
- 2.1 [Decouple State Management from UI](#21-decouple-state-management-from-ui)
|
||||
- 2.2 [Define Generic Context Interfaces for Dependency Injection](#22-define-generic-context-interfaces-for-dependency-injection)
|
||||
- 2.3 [Lift State into Provider Components](#23-lift-state-into-provider-components)
|
||||
3. [Implementation Patterns](#3-implementation-patterns) — **MEDIUM**
|
||||
- 3.1 [Create Explicit Component Variants](#31-create-explicit-component-variants)
|
||||
- 3.2 [Prefer Composing Children Over Render Props](#32-prefer-composing-children-over-render-props)
|
||||
4. [React 19 APIs](#4-react-19-apis) — **MEDIUM**
|
||||
- 4.1 [React 19 API Changes](#41-react-19-api-changes)
|
||||
|
||||
---
|
||||
|
||||
## 1. Component Architecture
|
||||
|
||||
**Impact: HIGH**
|
||||
|
||||
Fundamental patterns for structuring components to avoid prop
|
||||
proliferation and enable flexible composition.
|
||||
|
||||
### 1.1 Avoid Boolean Prop Proliferation
|
||||
|
||||
**Impact: CRITICAL (prevents unmaintainable component variants)**
|
||||
|
||||
Don't add boolean props like `isThread`, `isEditing`, `isDMThread` to customize
|
||||
|
||||
component behavior. Each boolean doubles possible states and creates
|
||||
|
||||
unmaintainable conditional logic. Use composition instead.
|
||||
|
||||
**Incorrect: boolean props create exponential complexity**
|
||||
|
||||
```tsx
|
||||
function Composer({
|
||||
onSubmit,
|
||||
isThread,
|
||||
channelId,
|
||||
isDMThread,
|
||||
dmId,
|
||||
isEditing,
|
||||
isForwarding,
|
||||
}: Props) {
|
||||
return (
|
||||
<form>
|
||||
<Header />
|
||||
<Input />
|
||||
{isDMThread ? (
|
||||
<AlsoSendToDMField id={dmId} />
|
||||
) : isThread ? (
|
||||
<AlsoSendToChannelField id={channelId} />
|
||||
) : null}
|
||||
{isEditing ? (
|
||||
<EditActions />
|
||||
) : isForwarding ? (
|
||||
<ForwardActions />
|
||||
) : (
|
||||
<DefaultActions />
|
||||
)}
|
||||
<Footer onSubmit={onSubmit} />
|
||||
</form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
**Correct: composition eliminates conditionals**
|
||||
|
||||
```tsx
|
||||
// Channel composer
|
||||
function ChannelComposer() {
|
||||
return (
|
||||
<Composer.Frame>
|
||||
<Composer.Header />
|
||||
<Composer.Input />
|
||||
<Composer.Footer>
|
||||
<Composer.Attachments />
|
||||
<Composer.Formatting />
|
||||
<Composer.Emojis />
|
||||
<Composer.Submit />
|
||||
</Composer.Footer>
|
||||
</Composer.Frame>
|
||||
)
|
||||
}
|
||||
|
||||
// Thread composer - adds "also send to channel" field
|
||||
function ThreadComposer({ channelId }: { channelId: string }) {
|
||||
return (
|
||||
<Composer.Frame>
|
||||
<Composer.Header />
|
||||
<Composer.Input />
|
||||
<AlsoSendToChannelField id={channelId} />
|
||||
<Composer.Footer>
|
||||
<Composer.Formatting />
|
||||
<Composer.Emojis />
|
||||
<Composer.Submit />
|
||||
</Composer.Footer>
|
||||
</Composer.Frame>
|
||||
)
|
||||
}
|
||||
|
||||
// Edit composer - different footer actions
|
||||
function EditComposer() {
|
||||
return (
|
||||
<Composer.Frame>
|
||||
<Composer.Input />
|
||||
<Composer.Footer>
|
||||
<Composer.Formatting />
|
||||
<Composer.Emojis />
|
||||
<Composer.CancelEdit />
|
||||
<Composer.SaveEdit />
|
||||
</Composer.Footer>
|
||||
</Composer.Frame>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
Each variant is explicit about what it renders. We can share internals without
|
||||
|
||||
sharing a single monolithic parent.
|
||||
|
||||
### 1.2 Use Compound Components
|
||||
|
||||
**Impact: HIGH (enables flexible composition without prop drilling)**
|
||||
|
||||
Structure complex components as compound components with a shared context. Each
|
||||
|
||||
subcomponent accesses shared state via context, not props. Consumers compose the
|
||||
|
||||
pieces they need.
|
||||
|
||||
**Incorrect: monolithic component with render props**
|
||||
|
||||
```tsx
|
||||
function Composer({
|
||||
renderHeader,
|
||||
renderFooter,
|
||||
renderActions,
|
||||
showAttachments,
|
||||
showFormatting,
|
||||
showEmojis,
|
||||
}: Props) {
|
||||
return (
|
||||
<form>
|
||||
{renderHeader?.()}
|
||||
<Input />
|
||||
{showAttachments && <Attachments />}
|
||||
{renderFooter ? (
|
||||
renderFooter()
|
||||
) : (
|
||||
<Footer>
|
||||
{showFormatting && <Formatting />}
|
||||
{showEmojis && <Emojis />}
|
||||
{renderActions?.()}
|
||||
</Footer>
|
||||
)}
|
||||
</form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
**Correct: compound components with shared context**
|
||||
|
||||
```tsx
|
||||
const ComposerContext = createContext<ComposerContextValue | null>(null)
|
||||
|
||||
function ComposerProvider({ children, state, actions, meta }: ProviderProps) {
|
||||
return (
|
||||
<ComposerContext value={{ state, actions, meta }}>
|
||||
{children}
|
||||
</ComposerContext>
|
||||
)
|
||||
}
|
||||
|
||||
function ComposerFrame({ children }: { children: React.ReactNode }) {
|
||||
return <form>{children}</form>
|
||||
}
|
||||
|
||||
function ComposerInput() {
|
||||
const {
|
||||
state,
|
||||
actions: { update },
|
||||
meta: { inputRef },
|
||||
} = use(ComposerContext)
|
||||
return (
|
||||
<TextInput
|
||||
ref={inputRef}
|
||||
value={state.input}
|
||||
onChangeText={(text) => update((s) => ({ ...s, input: text }))}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function ComposerSubmit() {
|
||||
const {
|
||||
actions: { submit },
|
||||
} = use(ComposerContext)
|
||||
return <Button onPress={submit}>Send</Button>
|
||||
}
|
||||
|
||||
// Export as compound component
|
||||
const Composer = {
|
||||
Provider: ComposerProvider,
|
||||
Frame: ComposerFrame,
|
||||
Input: ComposerInput,
|
||||
Submit: ComposerSubmit,
|
||||
Header: ComposerHeader,
|
||||
Footer: ComposerFooter,
|
||||
Attachments: ComposerAttachments,
|
||||
Formatting: ComposerFormatting,
|
||||
Emojis: ComposerEmojis,
|
||||
}
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
|
||||
```tsx
|
||||
<Composer.Provider state={state} actions={actions} meta={meta}>
|
||||
<Composer.Frame>
|
||||
<Composer.Header />
|
||||
<Composer.Input />
|
||||
<Composer.Footer>
|
||||
<Composer.Formatting />
|
||||
<Composer.Submit />
|
||||
</Composer.Footer>
|
||||
</Composer.Frame>
|
||||
</Composer.Provider>
|
||||
```
|
||||
|
||||
Consumers explicitly compose exactly what they need. No hidden conditionals. And the state, actions and meta are dependency-injected by a parent provider, allowing multiple usages of the same component structure.
|
||||
|
||||
---
|
||||
|
||||
## 2. State Management
|
||||
|
||||
**Impact: MEDIUM**
|
||||
|
||||
Patterns for lifting state and managing shared context across
|
||||
composed components.
|
||||
|
||||
### 2.1 Decouple State Management from UI
|
||||
|
||||
**Impact: MEDIUM (enables swapping state implementations without changing UI)**
|
||||
|
||||
The provider component should be the only place that knows how state is managed.
|
||||
|
||||
UI components consume the context interface—they don't know if state comes from
|
||||
|
||||
useState, Zustand, or a server sync.
|
||||
|
||||
**Incorrect: UI coupled to state implementation**
|
||||
|
||||
```tsx
|
||||
function ChannelComposer({ channelId }: { channelId: string }) {
|
||||
// UI component knows about global state implementation
|
||||
const state = useGlobalChannelState(channelId)
|
||||
const { submit, updateInput } = useChannelSync(channelId)
|
||||
|
||||
return (
|
||||
<Composer.Frame>
|
||||
<Composer.Input
|
||||
value={state.input}
|
||||
onChange={(text) => sync.updateInput(text)}
|
||||
/>
|
||||
<Composer.Submit onPress={() => sync.submit()} />
|
||||
</Composer.Frame>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
**Correct: state management isolated in provider**
|
||||
|
||||
```tsx
|
||||
// Provider handles all state management details
|
||||
function ChannelProvider({
|
||||
channelId,
|
||||
children,
|
||||
}: {
|
||||
channelId: string
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
const { state, update, submit } = useGlobalChannel(channelId)
|
||||
const inputRef = useRef(null)
|
||||
|
||||
return (
|
||||
<Composer.Provider
|
||||
state={state}
|
||||
actions={{ update, submit }}
|
||||
meta={{ inputRef }}
|
||||
>
|
||||
{children}
|
||||
</Composer.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
// UI component only knows about the context interface
|
||||
function ChannelComposer() {
|
||||
return (
|
||||
<Composer.Frame>
|
||||
<Composer.Header />
|
||||
<Composer.Input />
|
||||
<Composer.Footer>
|
||||
<Composer.Submit />
|
||||
</Composer.Footer>
|
||||
</Composer.Frame>
|
||||
)
|
||||
}
|
||||
|
||||
// Usage
|
||||
function Channel({ channelId }: { channelId: string }) {
|
||||
return (
|
||||
<ChannelProvider channelId={channelId}>
|
||||
<ChannelComposer />
|
||||
</ChannelProvider>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
**Different providers, same UI:**
|
||||
|
||||
```tsx
|
||||
// Local state for ephemeral forms
|
||||
function ForwardMessageProvider({ children }) {
|
||||
const [state, setState] = useState(initialState)
|
||||
const forwardMessage = useForwardMessage()
|
||||
|
||||
return (
|
||||
<Composer.Provider
|
||||
state={state}
|
||||
actions={{ update: setState, submit: forwardMessage }}
|
||||
>
|
||||
{children}
|
||||
</Composer.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
// Global synced state for channels
|
||||
function ChannelProvider({ channelId, children }) {
|
||||
const { state, update, submit } = useGlobalChannel(channelId)
|
||||
|
||||
return (
|
||||
<Composer.Provider state={state} actions={{ update, submit }}>
|
||||
{children}
|
||||
</Composer.Provider>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
The same `Composer.Input` component works with both providers because it only
|
||||
|
||||
depends on the context interface, not the implementation.
|
||||
|
||||
### 2.2 Define Generic Context Interfaces for Dependency Injection
|
||||
|
||||
**Impact: HIGH (enables dependency-injectable state across use-cases)**
|
||||
|
||||
Define a **generic interface** for your component context with three parts:
|
||||
|
||||
`state`, `actions`, and `meta`. This interface is a contract that any provider
|
||||
|
||||
can implement—enabling the same UI components to work with completely different
|
||||
|
||||
state implementations.
|
||||
|
||||
**Core principle:** Lift state, compose internals, make state
|
||||
|
||||
dependency-injectable.
|
||||
|
||||
**Incorrect: UI coupled to specific state implementation**
|
||||
|
||||
```tsx
|
||||
function ComposerInput() {
|
||||
// Tightly coupled to a specific hook
|
||||
const { input, setInput } = useChannelComposerState()
|
||||
return <TextInput value={input} onChangeText={setInput} />
|
||||
}
|
||||
```
|
||||
|
||||
**Correct: generic interface enables dependency injection**
|
||||
|
||||
```tsx
|
||||
// Define a GENERIC interface that any provider can implement
|
||||
interface ComposerState {
|
||||
input: string
|
||||
attachments: Attachment[]
|
||||
isSubmitting: boolean
|
||||
}
|
||||
|
||||
interface ComposerActions {
|
||||
update: (updater: (state: ComposerState) => ComposerState) => void
|
||||
submit: () => void
|
||||
}
|
||||
|
||||
interface ComposerMeta {
|
||||
inputRef: React.RefObject<TextInput>
|
||||
}
|
||||
|
||||
interface ComposerContextValue {
|
||||
state: ComposerState
|
||||
actions: ComposerActions
|
||||
meta: ComposerMeta
|
||||
}
|
||||
|
||||
const ComposerContext = createContext<ComposerContextValue | null>(null)
|
||||
```
|
||||
|
||||
**UI components consume the interface, not the implementation:**
|
||||
|
||||
```tsx
|
||||
function ComposerInput() {
|
||||
const {
|
||||
state,
|
||||
actions: { update },
|
||||
meta,
|
||||
} = use(ComposerContext)
|
||||
|
||||
// This component works with ANY provider that implements the interface
|
||||
return (
|
||||
<TextInput
|
||||
ref={meta.inputRef}
|
||||
value={state.input}
|
||||
onChangeText={(text) => update((s) => ({ ...s, input: text }))}
|
||||
/>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
**Different providers implement the same interface:**
|
||||
|
||||
```tsx
|
||||
// Provider A: Local state for ephemeral forms
|
||||
function ForwardMessageProvider({ children }: { children: React.ReactNode }) {
|
||||
const [state, setState] = useState(initialState)
|
||||
const inputRef = useRef(null)
|
||||
const submit = useForwardMessage()
|
||||
|
||||
return (
|
||||
<ComposerContext
|
||||
value={{
|
||||
state,
|
||||
actions: { update: setState, submit },
|
||||
meta: { inputRef },
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ComposerContext>
|
||||
)
|
||||
}
|
||||
|
||||
// Provider B: Global synced state for channels
|
||||
function ChannelProvider({ channelId, children }: Props) {
|
||||
const { state, update, submit } = useGlobalChannel(channelId)
|
||||
const inputRef = useRef(null)
|
||||
|
||||
return (
|
||||
<ComposerContext
|
||||
value={{
|
||||
state,
|
||||
actions: { update, submit },
|
||||
meta: { inputRef },
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ComposerContext>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
**The same composed UI works with both:**
|
||||
|
||||
```tsx
|
||||
// Works with ForwardMessageProvider (local state)
|
||||
<ForwardMessageProvider>
|
||||
<Composer.Frame>
|
||||
<Composer.Input />
|
||||
<Composer.Submit />
|
||||
</Composer.Frame>
|
||||
</ForwardMessageProvider>
|
||||
|
||||
// Works with ChannelProvider (global synced state)
|
||||
<ChannelProvider channelId="abc">
|
||||
<Composer.Frame>
|
||||
<Composer.Input />
|
||||
<Composer.Submit />
|
||||
</Composer.Frame>
|
||||
</ChannelProvider>
|
||||
```
|
||||
|
||||
**Custom UI outside the component can access state and actions:**
|
||||
|
||||
```tsx
|
||||
function ForwardMessageDialog() {
|
||||
return (
|
||||
<ForwardMessageProvider>
|
||||
<Dialog>
|
||||
{/* The composer UI */}
|
||||
<Composer.Frame>
|
||||
<Composer.Input placeholder="Add a message, if you'd like." />
|
||||
<Composer.Footer>
|
||||
<Composer.Formatting />
|
||||
<Composer.Emojis />
|
||||
</Composer.Footer>
|
||||
</Composer.Frame>
|
||||
|
||||
{/* Custom UI OUTSIDE the composer, but INSIDE the provider */}
|
||||
<MessagePreview />
|
||||
|
||||
{/* Actions at the bottom of the dialog */}
|
||||
<DialogActions>
|
||||
<CancelButton />
|
||||
<ForwardButton />
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</ForwardMessageProvider>
|
||||
)
|
||||
}
|
||||
|
||||
// This button lives OUTSIDE Composer.Frame but can still submit based on its context!
|
||||
function ForwardButton() {
|
||||
const {
|
||||
actions: { submit },
|
||||
} = use(ComposerContext)
|
||||
return <Button onPress={submit}>Forward</Button>
|
||||
}
|
||||
|
||||
// This preview lives OUTSIDE Composer.Frame but can read composer's state!
|
||||
function MessagePreview() {
|
||||
const { state } = use(ComposerContext)
|
||||
return <Preview message={state.input} attachments={state.attachments} />
|
||||
}
|
||||
```
|
||||
|
||||
The provider boundary is what matters—not the visual nesting. Components that
|
||||
|
||||
need shared state don't have to be inside the `Composer.Frame`. They just need
|
||||
|
||||
to be within the provider.
|
||||
|
||||
The `ForwardButton` and `MessagePreview` are not visually inside the composer
|
||||
|
||||
box, but they can still access its state and actions. This is the power of
|
||||
|
||||
lifting state into providers.
|
||||
|
||||
The UI is reusable bits you compose together. The state is dependency-injected
|
||||
|
||||
by the provider. Swap the provider, keep the UI.
|
||||
|
||||
### 2.3 Lift State into Provider Components
|
||||
|
||||
**Impact: HIGH (enables state sharing outside component boundaries)**
|
||||
|
||||
Move state management into dedicated provider components. This allows sibling
|
||||
|
||||
components outside the main UI to access and modify state without prop drilling
|
||||
|
||||
or awkward refs.
|
||||
|
||||
**Incorrect: state trapped inside component**
|
||||
|
||||
```tsx
|
||||
function ForwardMessageComposer() {
|
||||
const [state, setState] = useState(initialState)
|
||||
const forwardMessage = useForwardMessage()
|
||||
|
||||
return (
|
||||
<Composer.Frame>
|
||||
<Composer.Input />
|
||||
<Composer.Footer />
|
||||
</Composer.Frame>
|
||||
)
|
||||
}
|
||||
|
||||
// Problem: How does this button access composer state?
|
||||
function ForwardMessageDialog() {
|
||||
return (
|
||||
<Dialog>
|
||||
<ForwardMessageComposer />
|
||||
<MessagePreview /> {/* Needs composer state */}
|
||||
<DialogActions>
|
||||
<CancelButton />
|
||||
<ForwardButton /> {/* Needs to call submit */}
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
**Incorrect: useEffect to sync state up**
|
||||
|
||||
```tsx
|
||||
function ForwardMessageDialog() {
|
||||
const [input, setInput] = useState('')
|
||||
return (
|
||||
<Dialog>
|
||||
<ForwardMessageComposer onInputChange={setInput} />
|
||||
<MessagePreview input={input} />
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
function ForwardMessageComposer({ onInputChange }) {
|
||||
const [state, setState] = useState(initialState)
|
||||
useEffect(() => {
|
||||
onInputChange(state.input) // Sync on every change 😬
|
||||
}, [state.input])
|
||||
}
|
||||
```
|
||||
|
||||
**Incorrect: reading state from ref on submit**
|
||||
|
||||
```tsx
|
||||
function ForwardMessageDialog() {
|
||||
const stateRef = useRef(null)
|
||||
return (
|
||||
<Dialog>
|
||||
<ForwardMessageComposer stateRef={stateRef} />
|
||||
<ForwardButton onPress={() => submit(stateRef.current)} />
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
**Correct: state lifted to provider**
|
||||
|
||||
```tsx
|
||||
function ForwardMessageProvider({ children }: { children: React.ReactNode }) {
|
||||
const [state, setState] = useState(initialState)
|
||||
const forwardMessage = useForwardMessage()
|
||||
const inputRef = useRef(null)
|
||||
|
||||
return (
|
||||
<Composer.Provider
|
||||
state={state}
|
||||
actions={{ update: setState, submit: forwardMessage }}
|
||||
meta={{ inputRef }}
|
||||
>
|
||||
{children}
|
||||
</Composer.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
function ForwardMessageDialog() {
|
||||
return (
|
||||
<ForwardMessageProvider>
|
||||
<Dialog>
|
||||
<ForwardMessageComposer />
|
||||
<MessagePreview /> {/* Custom components can access state and actions */}
|
||||
<DialogActions>
|
||||
<CancelButton />
|
||||
<ForwardButton /> {/* Custom components can access state and actions */}
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</ForwardMessageProvider>
|
||||
)
|
||||
}
|
||||
|
||||
function ForwardButton() {
|
||||
const { actions } = use(Composer.Context)
|
||||
return <Button onPress={actions.submit}>Forward</Button>
|
||||
}
|
||||
```
|
||||
|
||||
The ForwardButton lives outside the Composer.Frame but still has access to the
|
||||
|
||||
submit action because it's within the provider. Even though it's a one-off
|
||||
|
||||
component, it can still access the composer's state and actions from outside the
|
||||
|
||||
UI itself.
|
||||
|
||||
**Key insight:** Components that need shared state don't have to be visually
|
||||
|
||||
nested inside each other—they just need to be within the same provider.
|
||||
|
||||
---
|
||||
|
||||
## 3. Implementation Patterns
|
||||
|
||||
**Impact: MEDIUM**
|
||||
|
||||
Specific techniques for implementing compound components and
|
||||
context providers.
|
||||
|
||||
### 3.1 Create Explicit Component Variants
|
||||
|
||||
**Impact: MEDIUM (self-documenting code, no hidden conditionals)**
|
||||
|
||||
Instead of one component with many boolean props, create explicit variant
|
||||
|
||||
components. Each variant composes the pieces it needs. The code documents
|
||||
|
||||
itself.
|
||||
|
||||
**Incorrect: one component, many modes**
|
||||
|
||||
```tsx
|
||||
// What does this component actually render?
|
||||
<Composer
|
||||
isThread
|
||||
isEditing={false}
|
||||
channelId='abc'
|
||||
showAttachments
|
||||
showFormatting={false}
|
||||
/>
|
||||
```
|
||||
|
||||
**Correct: explicit variants**
|
||||
|
||||
```tsx
|
||||
// Immediately clear what this renders
|
||||
<ThreadComposer channelId="abc" />
|
||||
|
||||
// Or
|
||||
<EditMessageComposer messageId="xyz" />
|
||||
|
||||
// Or
|
||||
<ForwardMessageComposer messageId="123" />
|
||||
```
|
||||
|
||||
Each implementation is unique, explicit and self-contained. Yet they can each
|
||||
|
||||
use shared parts.
|
||||
|
||||
**Implementation:**
|
||||
|
||||
```tsx
|
||||
function ThreadComposer({ channelId }: { channelId: string }) {
|
||||
return (
|
||||
<ThreadProvider channelId={channelId}>
|
||||
<Composer.Frame>
|
||||
<Composer.Input />
|
||||
<AlsoSendToChannelField channelId={channelId} />
|
||||
<Composer.Footer>
|
||||
<Composer.Formatting />
|
||||
<Composer.Emojis />
|
||||
<Composer.Submit />
|
||||
</Composer.Footer>
|
||||
</Composer.Frame>
|
||||
</ThreadProvider>
|
||||
)
|
||||
}
|
||||
|
||||
function EditMessageComposer({ messageId }: { messageId: string }) {
|
||||
return (
|
||||
<EditMessageProvider messageId={messageId}>
|
||||
<Composer.Frame>
|
||||
<Composer.Input />
|
||||
<Composer.Footer>
|
||||
<Composer.Formatting />
|
||||
<Composer.Emojis />
|
||||
<Composer.CancelEdit />
|
||||
<Composer.SaveEdit />
|
||||
</Composer.Footer>
|
||||
</Composer.Frame>
|
||||
</EditMessageProvider>
|
||||
)
|
||||
}
|
||||
|
||||
function ForwardMessageComposer({ messageId }: { messageId: string }) {
|
||||
return (
|
||||
<ForwardMessageProvider messageId={messageId}>
|
||||
<Composer.Frame>
|
||||
<Composer.Input placeholder="Add a message, if you'd like." />
|
||||
<Composer.Footer>
|
||||
<Composer.Formatting />
|
||||
<Composer.Emojis />
|
||||
<Composer.Mentions />
|
||||
</Composer.Footer>
|
||||
</Composer.Frame>
|
||||
</ForwardMessageProvider>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
Each variant is explicit about:
|
||||
|
||||
- What provider/state it uses
|
||||
|
||||
- What UI elements it includes
|
||||
|
||||
- What actions are available
|
||||
|
||||
No boolean prop combinations to reason about. No impossible states.
|
||||
|
||||
### 3.2 Prefer Composing Children Over Render Props
|
||||
|
||||
**Impact: MEDIUM (cleaner composition, better readability)**
|
||||
|
||||
Use `children` for composition instead of `renderX` props. Children are more
|
||||
|
||||
readable, compose naturally, and don't require understanding callback
|
||||
|
||||
signatures.
|
||||
|
||||
**Incorrect: render props**
|
||||
|
||||
```tsx
|
||||
function Composer({
|
||||
renderHeader,
|
||||
renderFooter,
|
||||
renderActions,
|
||||
}: {
|
||||
renderHeader?: () => React.ReactNode
|
||||
renderFooter?: () => React.ReactNode
|
||||
renderActions?: () => React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<form>
|
||||
{renderHeader?.()}
|
||||
<Input />
|
||||
{renderFooter ? renderFooter() : <DefaultFooter />}
|
||||
{renderActions?.()}
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
// Usage is awkward and inflexible
|
||||
return (
|
||||
<Composer
|
||||
renderHeader={() => <CustomHeader />}
|
||||
renderFooter={() => (
|
||||
<>
|
||||
<Formatting />
|
||||
<Emojis />
|
||||
</>
|
||||
)}
|
||||
renderActions={() => <SubmitButton />}
|
||||
/>
|
||||
)
|
||||
```
|
||||
|
||||
**Correct: compound components with children**
|
||||
|
||||
```tsx
|
||||
function ComposerFrame({ children }: { children: React.ReactNode }) {
|
||||
return <form>{children}</form>
|
||||
}
|
||||
|
||||
function ComposerFooter({ children }: { children: React.ReactNode }) {
|
||||
return <footer className='flex'>{children}</footer>
|
||||
}
|
||||
|
||||
// Usage is flexible
|
||||
return (
|
||||
<Composer.Frame>
|
||||
<CustomHeader />
|
||||
<Composer.Input />
|
||||
<Composer.Footer>
|
||||
<Composer.Formatting />
|
||||
<Composer.Emojis />
|
||||
<SubmitButton />
|
||||
</Composer.Footer>
|
||||
</Composer.Frame>
|
||||
)
|
||||
```
|
||||
|
||||
**When render props are appropriate:**
|
||||
|
||||
```tsx
|
||||
// Render props work well when you need to pass data back
|
||||
<List
|
||||
data={items}
|
||||
renderItem={({ item, index }) => <Item item={item} index={index} />}
|
||||
/>
|
||||
```
|
||||
|
||||
Use render props when the parent needs to provide data or state to the child.
|
||||
|
||||
Use children when composing static structure.
|
||||
|
||||
---
|
||||
|
||||
## 4. React 19 APIs
|
||||
|
||||
**Impact: MEDIUM**
|
||||
|
||||
React 19+ only. Don't use `forwardRef`; use `use()` instead of `useContext()`.
|
||||
|
||||
### 4.1 React 19 API Changes
|
||||
|
||||
**Impact: MEDIUM (cleaner component definitions and context usage)**
|
||||
|
||||
> **⚠️ React 19+ only.** Skip this if you're on React 18 or earlier.
|
||||
|
||||
In React 19, `ref` is now a regular prop (no `forwardRef` wrapper needed), and `use()` replaces `useContext()`.
|
||||
|
||||
**Incorrect: forwardRef in React 19**
|
||||
|
||||
```tsx
|
||||
const ComposerInput = forwardRef<TextInput, Props>((props, ref) => {
|
||||
return <TextInput ref={ref} {...props} />
|
||||
})
|
||||
```
|
||||
|
||||
**Correct: ref as a regular prop**
|
||||
|
||||
```tsx
|
||||
function ComposerInput({ ref, ...props }: Props & { ref?: React.Ref<TextInput> }) {
|
||||
return <TextInput ref={ref} {...props} />
|
||||
}
|
||||
```
|
||||
|
||||
**Incorrect: useContext in React 19**
|
||||
|
||||
```tsx
|
||||
const value = useContext(MyContext)
|
||||
```
|
||||
|
||||
**Correct: use instead of useContext**
|
||||
|
||||
```tsx
|
||||
const value = use(MyContext)
|
||||
```
|
||||
|
||||
`use()` can also be called conditionally, unlike `useContext()`.
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
1. [https://react.dev](https://react.dev)
|
||||
2. [https://react.dev/learn/passing-data-deeply-with-context](https://react.dev/learn/passing-data-deeply-with-context)
|
||||
3. [https://react.dev/reference/react/use](https://react.dev/reference/react/use)
|
||||
60
skills/composition-patterns/README.md
Normal file
60
skills/composition-patterns/README.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# React Composition Patterns
|
||||
|
||||
A structured repository for React composition patterns that scale. These
|
||||
patterns help avoid boolean prop proliferation by using compound components,
|
||||
lifting state, and composing internals.
|
||||
|
||||
## Structure
|
||||
|
||||
- `rules/` - Individual rule files (one per rule)
|
||||
- `_sections.md` - Section metadata (titles, impacts, descriptions)
|
||||
- `_template.md` - Template for creating new rules
|
||||
- `area-description.md` - Individual rule files
|
||||
- `metadata.json` - Document metadata (version, organization, abstract)
|
||||
- **`AGENTS.md`** - Compiled output (generated)
|
||||
|
||||
## Rules
|
||||
|
||||
### Component Architecture (CRITICAL)
|
||||
|
||||
- `architecture-avoid-boolean-props.md` - Don't add boolean props to customize
|
||||
behavior
|
||||
- `architecture-compound-components.md` - Structure as compound components with
|
||||
shared context
|
||||
|
||||
### State Management (HIGH)
|
||||
|
||||
- `state-lift-state.md` - Lift state into provider components
|
||||
- `state-context-interface.md` - Define clear context interfaces
|
||||
(state/actions/meta)
|
||||
- `state-decouple-implementation.md` - Decouple state management from UI
|
||||
|
||||
### Implementation Patterns (MEDIUM)
|
||||
|
||||
- `patterns-children-over-render-props.md` - Prefer children over renderX props
|
||||
- `patterns-explicit-variants.md` - Create explicit component variants
|
||||
|
||||
## Core Principles
|
||||
|
||||
1. **Composition over configuration** — Instead of adding props, let consumers
|
||||
compose
|
||||
2. **Lift your state** — State in providers, not trapped in components
|
||||
3. **Compose your internals** — Subcomponents access context, not props
|
||||
4. **Explicit variants** — Create ThreadComposer, EditComposer, not Composer
|
||||
with isThread
|
||||
|
||||
## Creating a New Rule
|
||||
|
||||
1. Copy `rules/_template.md` to `rules/area-description.md`
|
||||
2. Choose the appropriate area prefix:
|
||||
- `architecture-` for Component Architecture
|
||||
- `state-` for State Management
|
||||
- `patterns-` for Implementation Patterns
|
||||
3. Fill in the frontmatter and content
|
||||
4. Ensure you have clear examples with explanations
|
||||
|
||||
## Impact Levels
|
||||
|
||||
- `CRITICAL` - Foundational patterns, prevents unmaintainable code
|
||||
- `HIGH` - Significant maintainability improvements
|
||||
- `MEDIUM` - Good practices for cleaner code
|
||||
89
skills/composition-patterns/SKILL.md
Normal file
89
skills/composition-patterns/SKILL.md
Normal file
@@ -0,0 +1,89 @@
|
||||
---
|
||||
name: composition-patterns
|
||||
description:
|
||||
React composition patterns that scale. Use when refactoring components with
|
||||
boolean prop proliferation, building flexible component libraries, or
|
||||
designing reusable APIs. Triggers on tasks involving compound components,
|
||||
render props, context providers, or component architecture. Includes React 19
|
||||
API changes.
|
||||
license: MIT
|
||||
metadata:
|
||||
author: vercel
|
||||
version: '1.0.0'
|
||||
---
|
||||
|
||||
# React Composition Patterns
|
||||
|
||||
Composition patterns for building flexible, maintainable React components. Avoid
|
||||
boolean prop proliferation by using compound components, lifting state, and
|
||||
composing internals. These patterns make codebases easier for both humans and AI
|
||||
agents to work with as they scale.
|
||||
|
||||
## When to Apply
|
||||
|
||||
Reference these guidelines when:
|
||||
|
||||
- Refactoring components with many boolean props
|
||||
- Building reusable component libraries
|
||||
- Designing flexible component APIs
|
||||
- Reviewing component architecture
|
||||
- Working with compound components or context providers
|
||||
|
||||
## Rule Categories by Priority
|
||||
|
||||
| Priority | Category | Impact | Prefix |
|
||||
| -------- | ----------------------- | ------ | --------------- |
|
||||
| 1 | Component Architecture | HIGH | `architecture-` |
|
||||
| 2 | State Management | MEDIUM | `state-` |
|
||||
| 3 | Implementation Patterns | MEDIUM | `patterns-` |
|
||||
| 4 | React 19 APIs | MEDIUM | `react19-` |
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### 1. Component Architecture (HIGH)
|
||||
|
||||
- `architecture-avoid-boolean-props` - Don't add boolean props to customize
|
||||
behavior; use composition
|
||||
- `architecture-compound-components` - Structure complex components with shared
|
||||
context
|
||||
|
||||
### 2. State Management (MEDIUM)
|
||||
|
||||
- `state-decouple-implementation` - Provider is the only place that knows how
|
||||
state is managed
|
||||
- `state-context-interface` - Define generic interface with state, actions, meta
|
||||
for dependency injection
|
||||
- `state-lift-state` - Move state into provider components for sibling access
|
||||
|
||||
### 3. Implementation Patterns (MEDIUM)
|
||||
|
||||
- `patterns-explicit-variants` - Create explicit variant components instead of
|
||||
boolean modes
|
||||
- `patterns-children-over-render-props` - Use children for composition instead
|
||||
of renderX props
|
||||
|
||||
### 4. React 19 APIs (MEDIUM)
|
||||
|
||||
> **⚠️ React 19+ only.** Skip this section if using React 18 or earlier.
|
||||
|
||||
- `react19-no-forwardref` - Don't use `forwardRef`; use `use()` instead of `useContext()`
|
||||
|
||||
## How to Use
|
||||
|
||||
Read individual rule files for detailed explanations and code examples:
|
||||
|
||||
```
|
||||
rules/architecture-avoid-boolean-props.md
|
||||
rules/state-context-interface.md
|
||||
```
|
||||
|
||||
Each rule file contains:
|
||||
|
||||
- Brief explanation of why it matters
|
||||
- Incorrect code example with explanation
|
||||
- Correct code example with explanation
|
||||
- Additional context and references
|
||||
|
||||
## Full Compiled Document
|
||||
|
||||
For the complete guide with all rules expanded: `AGENTS.md`
|
||||
11
skills/composition-patterns/metadata.json
Normal file
11
skills/composition-patterns/metadata.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"organization": "Engineering",
|
||||
"date": "January 2026",
|
||||
"abstract": "Composition patterns for building flexible, maintainable React components. Avoid boolean prop proliferation by using compound components, lifting state, and composing internals. These patterns make codebases easier for both humans and AI agents to work with as they scale.",
|
||||
"references": [
|
||||
"https://react.dev",
|
||||
"https://react.dev/learn/passing-data-deeply-with-context",
|
||||
"https://react.dev/reference/react/use"
|
||||
]
|
||||
}
|
||||
29
skills/composition-patterns/rules/_sections.md
Normal file
29
skills/composition-patterns/rules/_sections.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Sections
|
||||
|
||||
This file defines all sections, their ordering, impact levels, and descriptions.
|
||||
The section ID (in parentheses) is the filename prefix used to group rules.
|
||||
|
||||
---
|
||||
|
||||
## 1. Component Architecture (architecture)
|
||||
|
||||
**Impact:** HIGH
|
||||
**Description:** Fundamental patterns for structuring components to avoid prop
|
||||
proliferation and enable flexible composition.
|
||||
|
||||
## 2. State Management (state)
|
||||
|
||||
**Impact:** MEDIUM
|
||||
**Description:** Patterns for lifting state and managing shared context across
|
||||
composed components.
|
||||
|
||||
## 3. Implementation Patterns (patterns)
|
||||
|
||||
**Impact:** MEDIUM
|
||||
**Description:** Specific techniques for implementing compound components and
|
||||
context providers.
|
||||
|
||||
## 4. React 19 APIs (react19)
|
||||
|
||||
**Impact:** MEDIUM
|
||||
**Description:** React 19+ only. Don't use `forwardRef`; use `use()` instead of `useContext()`.
|
||||
24
skills/composition-patterns/rules/_template.md
Normal file
24
skills/composition-patterns/rules/_template.md
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
title: Rule Title Here
|
||||
impact: MEDIUM
|
||||
impactDescription: brief description of impact
|
||||
tags: composition, components
|
||||
---
|
||||
|
||||
## Rule Title Here
|
||||
|
||||
Brief explanation of the rule and why it matters.
|
||||
|
||||
**Incorrect:**
|
||||
|
||||
```tsx
|
||||
// Bad code example
|
||||
```
|
||||
|
||||
**Correct:**
|
||||
|
||||
```tsx
|
||||
// Good code example
|
||||
```
|
||||
|
||||
Reference: [Link](https://example.com)
|
||||
@@ -0,0 +1,100 @@
|
||||
---
|
||||
title: Avoid Boolean Prop Proliferation
|
||||
impact: CRITICAL
|
||||
impactDescription: prevents unmaintainable component variants
|
||||
tags: composition, props, architecture
|
||||
---
|
||||
|
||||
## Avoid Boolean Prop Proliferation
|
||||
|
||||
Don't add boolean props like `isThread`, `isEditing`, `isDMThread` to customize
|
||||
component behavior. Each boolean doubles possible states and creates
|
||||
unmaintainable conditional logic. Use composition instead.
|
||||
|
||||
**Incorrect (boolean props create exponential complexity):**
|
||||
|
||||
```tsx
|
||||
function Composer({
|
||||
onSubmit,
|
||||
isThread,
|
||||
channelId,
|
||||
isDMThread,
|
||||
dmId,
|
||||
isEditing,
|
||||
isForwarding,
|
||||
}: Props) {
|
||||
return (
|
||||
<form>
|
||||
<Header />
|
||||
<Input />
|
||||
{isDMThread ? (
|
||||
<AlsoSendToDMField id={dmId} />
|
||||
) : isThread ? (
|
||||
<AlsoSendToChannelField id={channelId} />
|
||||
) : null}
|
||||
{isEditing ? (
|
||||
<EditActions />
|
||||
) : isForwarding ? (
|
||||
<ForwardActions />
|
||||
) : (
|
||||
<DefaultActions />
|
||||
)}
|
||||
<Footer onSubmit={onSubmit} />
|
||||
</form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
**Correct (composition eliminates conditionals):**
|
||||
|
||||
```tsx
|
||||
// Channel composer
|
||||
function ChannelComposer() {
|
||||
return (
|
||||
<Composer.Frame>
|
||||
<Composer.Header />
|
||||
<Composer.Input />
|
||||
<Composer.Footer>
|
||||
<Composer.Attachments />
|
||||
<Composer.Formatting />
|
||||
<Composer.Emojis />
|
||||
<Composer.Submit />
|
||||
</Composer.Footer>
|
||||
</Composer.Frame>
|
||||
)
|
||||
}
|
||||
|
||||
// Thread composer - adds "also send to channel" field
|
||||
function ThreadComposer({ channelId }: { channelId: string }) {
|
||||
return (
|
||||
<Composer.Frame>
|
||||
<Composer.Header />
|
||||
<Composer.Input />
|
||||
<AlsoSendToChannelField id={channelId} />
|
||||
<Composer.Footer>
|
||||
<Composer.Formatting />
|
||||
<Composer.Emojis />
|
||||
<Composer.Submit />
|
||||
</Composer.Footer>
|
||||
</Composer.Frame>
|
||||
)
|
||||
}
|
||||
|
||||
// Edit composer - different footer actions
|
||||
function EditComposer() {
|
||||
return (
|
||||
<Composer.Frame>
|
||||
<Composer.Input />
|
||||
<Composer.Footer>
|
||||
<Composer.Formatting />
|
||||
<Composer.Emojis />
|
||||
<Composer.CancelEdit />
|
||||
<Composer.SaveEdit />
|
||||
</Composer.Footer>
|
||||
</Composer.Frame>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
Each variant is explicit about what it renders. We can share internals without
|
||||
sharing a single monolithic parent.
|
||||
@@ -0,0 +1,112 @@
|
||||
---
|
||||
title: Use Compound Components
|
||||
impact: HIGH
|
||||
impactDescription: enables flexible composition without prop drilling
|
||||
tags: composition, compound-components, architecture
|
||||
---
|
||||
|
||||
## Use Compound Components
|
||||
|
||||
Structure complex components as compound components with a shared context. Each
|
||||
subcomponent accesses shared state via context, not props. Consumers compose the
|
||||
pieces they need.
|
||||
|
||||
**Incorrect (monolithic component with render props):**
|
||||
|
||||
```tsx
|
||||
function Composer({
|
||||
renderHeader,
|
||||
renderFooter,
|
||||
renderActions,
|
||||
showAttachments,
|
||||
showFormatting,
|
||||
showEmojis,
|
||||
}: Props) {
|
||||
return (
|
||||
<form>
|
||||
{renderHeader?.()}
|
||||
<Input />
|
||||
{showAttachments && <Attachments />}
|
||||
{renderFooter ? (
|
||||
renderFooter()
|
||||
) : (
|
||||
<Footer>
|
||||
{showFormatting && <Formatting />}
|
||||
{showEmojis && <Emojis />}
|
||||
{renderActions?.()}
|
||||
</Footer>
|
||||
)}
|
||||
</form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
**Correct (compound components with shared context):**
|
||||
|
||||
```tsx
|
||||
const ComposerContext = createContext<ComposerContextValue | null>(null)
|
||||
|
||||
function ComposerProvider({ children, state, actions, meta }: ProviderProps) {
|
||||
return (
|
||||
<ComposerContext value={{ state, actions, meta }}>
|
||||
{children}
|
||||
</ComposerContext>
|
||||
)
|
||||
}
|
||||
|
||||
function ComposerFrame({ children }: { children: React.ReactNode }) {
|
||||
return <form>{children}</form>
|
||||
}
|
||||
|
||||
function ComposerInput() {
|
||||
const {
|
||||
state,
|
||||
actions: { update },
|
||||
meta: { inputRef },
|
||||
} = use(ComposerContext)
|
||||
return (
|
||||
<TextInput
|
||||
ref={inputRef}
|
||||
value={state.input}
|
||||
onChangeText={(text) => update((s) => ({ ...s, input: text }))}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function ComposerSubmit() {
|
||||
const {
|
||||
actions: { submit },
|
||||
} = use(ComposerContext)
|
||||
return <Button onPress={submit}>Send</Button>
|
||||
}
|
||||
|
||||
// Export as compound component
|
||||
const Composer = {
|
||||
Provider: ComposerProvider,
|
||||
Frame: ComposerFrame,
|
||||
Input: ComposerInput,
|
||||
Submit: ComposerSubmit,
|
||||
Header: ComposerHeader,
|
||||
Footer: ComposerFooter,
|
||||
Attachments: ComposerAttachments,
|
||||
Formatting: ComposerFormatting,
|
||||
Emojis: ComposerEmojis,
|
||||
}
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
|
||||
```tsx
|
||||
<Composer.Provider state={state} actions={actions} meta={meta}>
|
||||
<Composer.Frame>
|
||||
<Composer.Header />
|
||||
<Composer.Input />
|
||||
<Composer.Footer>
|
||||
<Composer.Formatting />
|
||||
<Composer.Submit />
|
||||
</Composer.Footer>
|
||||
</Composer.Frame>
|
||||
</Composer.Provider>
|
||||
```
|
||||
|
||||
Consumers explicitly compose exactly what they need. No hidden conditionals. And the state, actions and meta are dependency-injected by a parent provider, allowing multiple usages of the same component structure.
|
||||
@@ -0,0 +1,87 @@
|
||||
---
|
||||
title: Prefer Composing Children Over Render Props
|
||||
impact: MEDIUM
|
||||
impactDescription: cleaner composition, better readability
|
||||
tags: composition, children, render-props
|
||||
---
|
||||
|
||||
## Prefer Children Over Render Props
|
||||
|
||||
Use `children` for composition instead of `renderX` props. Children are more
|
||||
readable, compose naturally, and don't require understanding callback
|
||||
signatures.
|
||||
|
||||
**Incorrect (render props):**
|
||||
|
||||
```tsx
|
||||
function Composer({
|
||||
renderHeader,
|
||||
renderFooter,
|
||||
renderActions,
|
||||
}: {
|
||||
renderHeader?: () => React.ReactNode
|
||||
renderFooter?: () => React.ReactNode
|
||||
renderActions?: () => React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<form>
|
||||
{renderHeader?.()}
|
||||
<Input />
|
||||
{renderFooter ? renderFooter() : <DefaultFooter />}
|
||||
{renderActions?.()}
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
// Usage is awkward and inflexible
|
||||
return (
|
||||
<Composer
|
||||
renderHeader={() => <CustomHeader />}
|
||||
renderFooter={() => (
|
||||
<>
|
||||
<Formatting />
|
||||
<Emojis />
|
||||
</>
|
||||
)}
|
||||
renderActions={() => <SubmitButton />}
|
||||
/>
|
||||
)
|
||||
```
|
||||
|
||||
**Correct (compound components with children):**
|
||||
|
||||
```tsx
|
||||
function ComposerFrame({ children }: { children: React.ReactNode }) {
|
||||
return <form>{children}</form>
|
||||
}
|
||||
|
||||
function ComposerFooter({ children }: { children: React.ReactNode }) {
|
||||
return <footer className='flex'>{children}</footer>
|
||||
}
|
||||
|
||||
// Usage is flexible
|
||||
return (
|
||||
<Composer.Frame>
|
||||
<CustomHeader />
|
||||
<Composer.Input />
|
||||
<Composer.Footer>
|
||||
<Composer.Formatting />
|
||||
<Composer.Emojis />
|
||||
<SubmitButton />
|
||||
</Composer.Footer>
|
||||
</Composer.Frame>
|
||||
)
|
||||
```
|
||||
|
||||
**When render props are appropriate:**
|
||||
|
||||
```tsx
|
||||
// Render props work well when you need to pass data back
|
||||
<List
|
||||
data={items}
|
||||
renderItem={({ item, index }) => <Item item={item} index={index} />}
|
||||
/>
|
||||
```
|
||||
|
||||
Use render props when the parent needs to provide data or state to the child.
|
||||
Use children when composing static structure.
|
||||
100
skills/composition-patterns/rules/patterns-explicit-variants.md
Normal file
100
skills/composition-patterns/rules/patterns-explicit-variants.md
Normal file
@@ -0,0 +1,100 @@
|
||||
---
|
||||
title: Create Explicit Component Variants
|
||||
impact: MEDIUM
|
||||
impactDescription: self-documenting code, no hidden conditionals
|
||||
tags: composition, variants, architecture
|
||||
---
|
||||
|
||||
## Create Explicit Component Variants
|
||||
|
||||
Instead of one component with many boolean props, create explicit variant
|
||||
components. Each variant composes the pieces it needs. The code documents
|
||||
itself.
|
||||
|
||||
**Incorrect (one component, many modes):**
|
||||
|
||||
```tsx
|
||||
// What does this component actually render?
|
||||
<Composer
|
||||
isThread
|
||||
isEditing={false}
|
||||
channelId='abc'
|
||||
showAttachments
|
||||
showFormatting={false}
|
||||
/>
|
||||
```
|
||||
|
||||
**Correct (explicit variants):**
|
||||
|
||||
```tsx
|
||||
// Immediately clear what this renders
|
||||
<ThreadComposer channelId="abc" />
|
||||
|
||||
// Or
|
||||
<EditMessageComposer messageId="xyz" />
|
||||
|
||||
// Or
|
||||
<ForwardMessageComposer messageId="123" />
|
||||
```
|
||||
|
||||
Each implementation is unique, explicit and self-contained. Yet they can each
|
||||
use shared parts.
|
||||
|
||||
**Implementation:**
|
||||
|
||||
```tsx
|
||||
function ThreadComposer({ channelId }: { channelId: string }) {
|
||||
return (
|
||||
<ThreadProvider channelId={channelId}>
|
||||
<Composer.Frame>
|
||||
<Composer.Input />
|
||||
<AlsoSendToChannelField channelId={channelId} />
|
||||
<Composer.Footer>
|
||||
<Composer.Formatting />
|
||||
<Composer.Emojis />
|
||||
<Composer.Submit />
|
||||
</Composer.Footer>
|
||||
</Composer.Frame>
|
||||
</ThreadProvider>
|
||||
)
|
||||
}
|
||||
|
||||
function EditMessageComposer({ messageId }: { messageId: string }) {
|
||||
return (
|
||||
<EditMessageProvider messageId={messageId}>
|
||||
<Composer.Frame>
|
||||
<Composer.Input />
|
||||
<Composer.Footer>
|
||||
<Composer.Formatting />
|
||||
<Composer.Emojis />
|
||||
<Composer.CancelEdit />
|
||||
<Composer.SaveEdit />
|
||||
</Composer.Footer>
|
||||
</Composer.Frame>
|
||||
</EditMessageProvider>
|
||||
)
|
||||
}
|
||||
|
||||
function ForwardMessageComposer({ messageId }: { messageId: string }) {
|
||||
return (
|
||||
<ForwardMessageProvider messageId={messageId}>
|
||||
<Composer.Frame>
|
||||
<Composer.Input placeholder="Add a message, if you'd like." />
|
||||
<Composer.Footer>
|
||||
<Composer.Formatting />
|
||||
<Composer.Emojis />
|
||||
<Composer.Mentions />
|
||||
</Composer.Footer>
|
||||
</Composer.Frame>
|
||||
</ForwardMessageProvider>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
Each variant is explicit about:
|
||||
|
||||
- What provider/state it uses
|
||||
- What UI elements it includes
|
||||
- What actions are available
|
||||
|
||||
No boolean prop combinations to reason about. No impossible states.
|
||||
42
skills/composition-patterns/rules/react19-no-forwardref.md
Normal file
42
skills/composition-patterns/rules/react19-no-forwardref.md
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
title: React 19 API Changes
|
||||
impact: MEDIUM
|
||||
impactDescription: cleaner component definitions and context usage
|
||||
tags: react19, refs, context, hooks
|
||||
---
|
||||
|
||||
## React 19 API Changes
|
||||
|
||||
> **⚠️ React 19+ only.** Skip this if you're on React 18 or earlier.
|
||||
|
||||
In React 19, `ref` is now a regular prop (no `forwardRef` wrapper needed), and `use()` replaces `useContext()`.
|
||||
|
||||
**Incorrect (forwardRef in React 19):**
|
||||
|
||||
```tsx
|
||||
const ComposerInput = forwardRef<TextInput, Props>((props, ref) => {
|
||||
return <TextInput ref={ref} {...props} />
|
||||
})
|
||||
```
|
||||
|
||||
**Correct (ref as a regular prop):**
|
||||
|
||||
```tsx
|
||||
function ComposerInput({ ref, ...props }: Props & { ref?: React.Ref<TextInput> }) {
|
||||
return <TextInput ref={ref} {...props} />
|
||||
}
|
||||
```
|
||||
|
||||
**Incorrect (useContext in React 19):**
|
||||
|
||||
```tsx
|
||||
const value = useContext(MyContext)
|
||||
```
|
||||
|
||||
**Correct (use instead of useContext):**
|
||||
|
||||
```tsx
|
||||
const value = use(MyContext)
|
||||
```
|
||||
|
||||
`use()` can also be called conditionally, unlike `useContext()`.
|
||||
191
skills/composition-patterns/rules/state-context-interface.md
Normal file
191
skills/composition-patterns/rules/state-context-interface.md
Normal file
@@ -0,0 +1,191 @@
|
||||
---
|
||||
title: Define Generic Context Interfaces for Dependency Injection
|
||||
impact: HIGH
|
||||
impactDescription: enables dependency-injectable state across use-cases
|
||||
tags: composition, context, state, typescript, dependency-injection
|
||||
---
|
||||
|
||||
## Define Generic Context Interfaces for Dependency Injection
|
||||
|
||||
Define a **generic interface** for your component context with three parts:
|
||||
`state`, `actions`, and `meta`. This interface is a contract that any provider
|
||||
can implement—enabling the same UI components to work with completely different
|
||||
state implementations.
|
||||
|
||||
**Core principle:** Lift state, compose internals, make state
|
||||
dependency-injectable.
|
||||
|
||||
**Incorrect (UI coupled to specific state implementation):**
|
||||
|
||||
```tsx
|
||||
function ComposerInput() {
|
||||
// Tightly coupled to a specific hook
|
||||
const { input, setInput } = useChannelComposerState()
|
||||
return <TextInput value={input} onChangeText={setInput} />
|
||||
}
|
||||
```
|
||||
|
||||
**Correct (generic interface enables dependency injection):**
|
||||
|
||||
```tsx
|
||||
// Define a GENERIC interface that any provider can implement
|
||||
interface ComposerState {
|
||||
input: string
|
||||
attachments: Attachment[]
|
||||
isSubmitting: boolean
|
||||
}
|
||||
|
||||
interface ComposerActions {
|
||||
update: (updater: (state: ComposerState) => ComposerState) => void
|
||||
submit: () => void
|
||||
}
|
||||
|
||||
interface ComposerMeta {
|
||||
inputRef: React.RefObject<TextInput>
|
||||
}
|
||||
|
||||
interface ComposerContextValue {
|
||||
state: ComposerState
|
||||
actions: ComposerActions
|
||||
meta: ComposerMeta
|
||||
}
|
||||
|
||||
const ComposerContext = createContext<ComposerContextValue | null>(null)
|
||||
```
|
||||
|
||||
**UI components consume the interface, not the implementation:**
|
||||
|
||||
```tsx
|
||||
function ComposerInput() {
|
||||
const {
|
||||
state,
|
||||
actions: { update },
|
||||
meta,
|
||||
} = use(ComposerContext)
|
||||
|
||||
// This component works with ANY provider that implements the interface
|
||||
return (
|
||||
<TextInput
|
||||
ref={meta.inputRef}
|
||||
value={state.input}
|
||||
onChangeText={(text) => update((s) => ({ ...s, input: text }))}
|
||||
/>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
**Different providers implement the same interface:**
|
||||
|
||||
```tsx
|
||||
// Provider A: Local state for ephemeral forms
|
||||
function ForwardMessageProvider({ children }: { children: React.ReactNode }) {
|
||||
const [state, setState] = useState(initialState)
|
||||
const inputRef = useRef(null)
|
||||
const submit = useForwardMessage()
|
||||
|
||||
return (
|
||||
<ComposerContext
|
||||
value={{
|
||||
state,
|
||||
actions: { update: setState, submit },
|
||||
meta: { inputRef },
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ComposerContext>
|
||||
)
|
||||
}
|
||||
|
||||
// Provider B: Global synced state for channels
|
||||
function ChannelProvider({ channelId, children }: Props) {
|
||||
const { state, update, submit } = useGlobalChannel(channelId)
|
||||
const inputRef = useRef(null)
|
||||
|
||||
return (
|
||||
<ComposerContext
|
||||
value={{
|
||||
state,
|
||||
actions: { update, submit },
|
||||
meta: { inputRef },
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ComposerContext>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
**The same composed UI works with both:**
|
||||
|
||||
```tsx
|
||||
// Works with ForwardMessageProvider (local state)
|
||||
<ForwardMessageProvider>
|
||||
<Composer.Frame>
|
||||
<Composer.Input />
|
||||
<Composer.Submit />
|
||||
</Composer.Frame>
|
||||
</ForwardMessageProvider>
|
||||
|
||||
// Works with ChannelProvider (global synced state)
|
||||
<ChannelProvider channelId="abc">
|
||||
<Composer.Frame>
|
||||
<Composer.Input />
|
||||
<Composer.Submit />
|
||||
</Composer.Frame>
|
||||
</ChannelProvider>
|
||||
```
|
||||
|
||||
**Custom UI outside the component can access state and actions:**
|
||||
|
||||
The provider boundary is what matters—not the visual nesting. Components that
|
||||
need shared state don't have to be inside the `Composer.Frame`. They just need
|
||||
to be within the provider.
|
||||
|
||||
```tsx
|
||||
function ForwardMessageDialog() {
|
||||
return (
|
||||
<ForwardMessageProvider>
|
||||
<Dialog>
|
||||
{/* The composer UI */}
|
||||
<Composer.Frame>
|
||||
<Composer.Input placeholder="Add a message, if you'd like." />
|
||||
<Composer.Footer>
|
||||
<Composer.Formatting />
|
||||
<Composer.Emojis />
|
||||
</Composer.Footer>
|
||||
</Composer.Frame>
|
||||
|
||||
{/* Custom UI OUTSIDE the composer, but INSIDE the provider */}
|
||||
<MessagePreview />
|
||||
|
||||
{/* Actions at the bottom of the dialog */}
|
||||
<DialogActions>
|
||||
<CancelButton />
|
||||
<ForwardButton />
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</ForwardMessageProvider>
|
||||
)
|
||||
}
|
||||
|
||||
// This button lives OUTSIDE Composer.Frame but can still submit based on its context!
|
||||
function ForwardButton() {
|
||||
const {
|
||||
actions: { submit },
|
||||
} = use(ComposerContext)
|
||||
return <Button onPress={submit}>Forward</Button>
|
||||
}
|
||||
|
||||
// This preview lives OUTSIDE Composer.Frame but can read composer's state!
|
||||
function MessagePreview() {
|
||||
const { state } = use(ComposerContext)
|
||||
return <Preview message={state.input} attachments={state.attachments} />
|
||||
}
|
||||
```
|
||||
|
||||
The `ForwardButton` and `MessagePreview` are not visually inside the composer
|
||||
box, but they can still access its state and actions. This is the power of
|
||||
lifting state into providers.
|
||||
|
||||
The UI is reusable bits you compose together. The state is dependency-injected
|
||||
by the provider. Swap the provider, keep the UI.
|
||||
@@ -0,0 +1,113 @@
|
||||
---
|
||||
title: Decouple State Management from UI
|
||||
impact: MEDIUM
|
||||
impactDescription: enables swapping state implementations without changing UI
|
||||
tags: composition, state, architecture
|
||||
---
|
||||
|
||||
## Decouple State Management from UI
|
||||
|
||||
The provider component should be the only place that knows how state is managed.
|
||||
UI components consume the context interface—they don't know if state comes from
|
||||
useState, Zustand, or a server sync.
|
||||
|
||||
**Incorrect (UI coupled to state implementation):**
|
||||
|
||||
```tsx
|
||||
function ChannelComposer({ channelId }: { channelId: string }) {
|
||||
// UI component knows about global state implementation
|
||||
const state = useGlobalChannelState(channelId)
|
||||
const { submit, updateInput } = useChannelSync(channelId)
|
||||
|
||||
return (
|
||||
<Composer.Frame>
|
||||
<Composer.Input
|
||||
value={state.input}
|
||||
onChange={(text) => sync.updateInput(text)}
|
||||
/>
|
||||
<Composer.Submit onPress={() => sync.submit()} />
|
||||
</Composer.Frame>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
**Correct (state management isolated in provider):**
|
||||
|
||||
```tsx
|
||||
// Provider handles all state management details
|
||||
function ChannelProvider({
|
||||
channelId,
|
||||
children,
|
||||
}: {
|
||||
channelId: string
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
const { state, update, submit } = useGlobalChannel(channelId)
|
||||
const inputRef = useRef(null)
|
||||
|
||||
return (
|
||||
<Composer.Provider
|
||||
state={state}
|
||||
actions={{ update, submit }}
|
||||
meta={{ inputRef }}
|
||||
>
|
||||
{children}
|
||||
</Composer.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
// UI component only knows about the context interface
|
||||
function ChannelComposer() {
|
||||
return (
|
||||
<Composer.Frame>
|
||||
<Composer.Header />
|
||||
<Composer.Input />
|
||||
<Composer.Footer>
|
||||
<Composer.Submit />
|
||||
</Composer.Footer>
|
||||
</Composer.Frame>
|
||||
)
|
||||
}
|
||||
|
||||
// Usage
|
||||
function Channel({ channelId }: { channelId: string }) {
|
||||
return (
|
||||
<ChannelProvider channelId={channelId}>
|
||||
<ChannelComposer />
|
||||
</ChannelProvider>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
**Different providers, same UI:**
|
||||
|
||||
```tsx
|
||||
// Local state for ephemeral forms
|
||||
function ForwardMessageProvider({ children }) {
|
||||
const [state, setState] = useState(initialState)
|
||||
const forwardMessage = useForwardMessage()
|
||||
|
||||
return (
|
||||
<Composer.Provider
|
||||
state={state}
|
||||
actions={{ update: setState, submit: forwardMessage }}
|
||||
>
|
||||
{children}
|
||||
</Composer.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
// Global synced state for channels
|
||||
function ChannelProvider({ channelId, children }) {
|
||||
const { state, update, submit } = useGlobalChannel(channelId)
|
||||
|
||||
return (
|
||||
<Composer.Provider state={state} actions={{ update, submit }}>
|
||||
{children}
|
||||
</Composer.Provider>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
The same `Composer.Input` component works with both providers because it only
|
||||
depends on the context interface, not the implementation.
|
||||
125
skills/composition-patterns/rules/state-lift-state.md
Normal file
125
skills/composition-patterns/rules/state-lift-state.md
Normal file
@@ -0,0 +1,125 @@
|
||||
---
|
||||
title: Lift State into Provider Components
|
||||
impact: HIGH
|
||||
impactDescription: enables state sharing outside component boundaries
|
||||
tags: composition, state, context, providers
|
||||
---
|
||||
|
||||
## Lift State into Provider Components
|
||||
|
||||
Move state management into dedicated provider components. This allows sibling
|
||||
components outside the main UI to access and modify state without prop drilling
|
||||
or awkward refs.
|
||||
|
||||
**Incorrect (state trapped inside component):**
|
||||
|
||||
```tsx
|
||||
function ForwardMessageComposer() {
|
||||
const [state, setState] = useState(initialState)
|
||||
const forwardMessage = useForwardMessage()
|
||||
|
||||
return (
|
||||
<Composer.Frame>
|
||||
<Composer.Input />
|
||||
<Composer.Footer />
|
||||
</Composer.Frame>
|
||||
)
|
||||
}
|
||||
|
||||
// Problem: How does this button access composer state?
|
||||
function ForwardMessageDialog() {
|
||||
return (
|
||||
<Dialog>
|
||||
<ForwardMessageComposer />
|
||||
<MessagePreview /> {/* Needs composer state */}
|
||||
<DialogActions>
|
||||
<CancelButton />
|
||||
<ForwardButton /> {/* Needs to call submit */}
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
**Incorrect (useEffect to sync state up):**
|
||||
|
||||
```tsx
|
||||
function ForwardMessageDialog() {
|
||||
const [input, setInput] = useState('')
|
||||
return (
|
||||
<Dialog>
|
||||
<ForwardMessageComposer onInputChange={setInput} />
|
||||
<MessagePreview input={input} />
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
function ForwardMessageComposer({ onInputChange }) {
|
||||
const [state, setState] = useState(initialState)
|
||||
useEffect(() => {
|
||||
onInputChange(state.input) // Sync on every change 😬
|
||||
}, [state.input])
|
||||
}
|
||||
```
|
||||
|
||||
**Incorrect (reading state from ref on submit):**
|
||||
|
||||
```tsx
|
||||
function ForwardMessageDialog() {
|
||||
const stateRef = useRef(null)
|
||||
return (
|
||||
<Dialog>
|
||||
<ForwardMessageComposer stateRef={stateRef} />
|
||||
<ForwardButton onPress={() => submit(stateRef.current)} />
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
**Correct (state lifted to provider):**
|
||||
|
||||
```tsx
|
||||
function ForwardMessageProvider({ children }: { children: React.ReactNode }) {
|
||||
const [state, setState] = useState(initialState)
|
||||
const forwardMessage = useForwardMessage()
|
||||
const inputRef = useRef(null)
|
||||
|
||||
return (
|
||||
<Composer.Provider
|
||||
state={state}
|
||||
actions={{ update: setState, submit: forwardMessage }}
|
||||
meta={{ inputRef }}
|
||||
>
|
||||
{children}
|
||||
</Composer.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
function ForwardMessageDialog() {
|
||||
return (
|
||||
<ForwardMessageProvider>
|
||||
<Dialog>
|
||||
<ForwardMessageComposer />
|
||||
<MessagePreview /> {/* Custom components can access state and actions */}
|
||||
<DialogActions>
|
||||
<CancelButton />
|
||||
<ForwardButton /> {/* Custom components can access state and actions */}
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</ForwardMessageProvider>
|
||||
)
|
||||
}
|
||||
|
||||
function ForwardButton() {
|
||||
const { actions } = use(Composer.Context)
|
||||
return <Button onPress={actions.submit}>Forward</Button>
|
||||
}
|
||||
```
|
||||
|
||||
The ForwardButton lives outside the Composer.Frame but still has access to the
|
||||
submit action because it's within the provider. Even though it's a one-off
|
||||
component, it can still access the composer's state and actions from outside the
|
||||
UI itself.
|
||||
|
||||
**Key insight:** Components that need shared state don't have to be visually
|
||||
nested inside each other—they just need to be within the same provider.
|
||||
828
skills/controlled-ux-designer/ACCESSIBILITY.md
Normal file
828
skills/controlled-ux-designer/ACCESSIBILITY.md
Normal file
@@ -0,0 +1,828 @@
|
||||
# Accessibility Reference
|
||||
|
||||
Comprehensive guide for implementing accessible interfaces following WCAG 2.1 AA standards.
|
||||
|
||||
## Core Principles (POUR)
|
||||
|
||||
### Perceivable
|
||||
Information and UI components must be presentable to users in ways they can perceive.
|
||||
|
||||
### Operable
|
||||
UI components and navigation must be operable by all users.
|
||||
|
||||
### Understandable
|
||||
Information and the operation of UI must be understandable.
|
||||
|
||||
### Robust
|
||||
Content must be robust enough to be interpreted by a wide variety of user agents, including assistive technologies.
|
||||
|
||||
## Semantic HTML
|
||||
|
||||
### Use Appropriate Elements
|
||||
|
||||
**Good:**
|
||||
```tsx
|
||||
<header>
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="/">Home</a></li>
|
||||
<li><a href="/about">About</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<article>
|
||||
<h1>Article Title</h1>
|
||||
<p>Article content...</p>
|
||||
</article>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>© 2025 Company Name</p>
|
||||
</footer>
|
||||
```
|
||||
|
||||
**Bad:**
|
||||
```tsx
|
||||
<div class="header">
|
||||
<div class="nav">
|
||||
<div class="link">Home</div>
|
||||
<div class="link">About</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Heading Hierarchy
|
||||
|
||||
**Correct hierarchy:**
|
||||
```tsx
|
||||
<h1>Page Title</h1>
|
||||
<h2>Section 1</h2>
|
||||
<h3>Subsection 1.1</h3>
|
||||
<h3>Subsection 1.2</h3>
|
||||
<h2>Section 2</h2>
|
||||
<h3>Subsection 2.1</h3>
|
||||
```
|
||||
|
||||
**Incorrect (skips levels):**
|
||||
```tsx
|
||||
<h1>Page Title</h1>
|
||||
<h4>Section 1</h4> // ❌ Skips h2 and h3
|
||||
```
|
||||
|
||||
## Keyboard Navigation
|
||||
|
||||
### Focus Management
|
||||
|
||||
```tsx
|
||||
// Ensure all interactive elements are keyboard accessible
|
||||
<button
|
||||
className="
|
||||
px-4 py-2
|
||||
focus:outline-none
|
||||
focus:ring-4 focus:ring-blue-500
|
||||
focus:ring-offset-2
|
||||
rounded-lg
|
||||
"
|
||||
tabIndex={0}
|
||||
>
|
||||
Accessible Button
|
||||
</button>
|
||||
|
||||
// Custom interactive elements need tabindex
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={handleClick}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
handleClick();
|
||||
}
|
||||
}}
|
||||
className="cursor-pointer focus:ring-4 focus:ring-blue-500"
|
||||
>
|
||||
Custom Button
|
||||
</div>
|
||||
```
|
||||
|
||||
### Tab Order
|
||||
|
||||
```tsx
|
||||
// Use tabIndex to control focus order
|
||||
<form>
|
||||
<input tabIndex={1} aria-label="First name" />
|
||||
<input tabIndex={2} aria-label="Last name" />
|
||||
<input tabIndex={3} aria-label="Email" />
|
||||
<button tabIndex={4}>Submit</button>
|
||||
</form>
|
||||
|
||||
// Use tabIndex={-1} to remove from tab order but allow programmatic focus
|
||||
<div tabIndex={-1} id="error-message">
|
||||
Error details...
|
||||
</div>
|
||||
```
|
||||
|
||||
### Skip Links
|
||||
|
||||
```tsx
|
||||
// Allow keyboard users to skip to main content
|
||||
<a
|
||||
href="#main-content"
|
||||
className="
|
||||
sr-only
|
||||
focus:not-sr-only
|
||||
focus:absolute
|
||||
focus:top-4 focus:left-4
|
||||
focus:z-50
|
||||
focus:px-4 focus:py-2
|
||||
focus:bg-blue-600 focus:text-white
|
||||
focus:rounded-lg
|
||||
"
|
||||
>
|
||||
Skip to main content
|
||||
</a>
|
||||
|
||||
<main id="main-content">
|
||||
{/* Main content */}
|
||||
</main>
|
||||
```
|
||||
|
||||
## ARIA Attributes
|
||||
|
||||
### Common ARIA Roles
|
||||
|
||||
```tsx
|
||||
// Navigation landmark
|
||||
<nav role="navigation" aria-label="Main navigation">
|
||||
{/* Navigation items */}
|
||||
</nav>
|
||||
|
||||
// Banner (header)
|
||||
<header role="banner">
|
||||
{/* Header content */}
|
||||
</header>
|
||||
|
||||
// Main content
|
||||
<main role="main">
|
||||
{/* Main content */}
|
||||
</main>
|
||||
|
||||
// Complementary (sidebar)
|
||||
<aside role="complementary" aria-label="Related articles">
|
||||
{/* Sidebar content */}
|
||||
</aside>
|
||||
|
||||
// Content info (footer)
|
||||
<footer role="contentinfo">
|
||||
{/* Footer content */}
|
||||
</footer>
|
||||
|
||||
// Search
|
||||
<form role="search" aria-label="Site search">
|
||||
<input type="search" aria-label="Search query" />
|
||||
<button type="submit">Search</button>
|
||||
</form>
|
||||
```
|
||||
|
||||
### ARIA Labels
|
||||
|
||||
```tsx
|
||||
// aria-label for elements without visible text
|
||||
<button aria-label="Close dialog">
|
||||
<X size={24} />
|
||||
</button>
|
||||
|
||||
// aria-labelledby to reference another element
|
||||
<div role="dialog" aria-labelledby="dialog-title">
|
||||
<h2 id="dialog-title">Confirm Action</h2>
|
||||
<p>Are you sure you want to continue?</p>
|
||||
</div>
|
||||
|
||||
// aria-describedby for additional description
|
||||
<input
|
||||
type="password"
|
||||
aria-describedby="password-requirements"
|
||||
/>
|
||||
<p id="password-requirements">
|
||||
Password must be at least 8 characters
|
||||
</p>
|
||||
```
|
||||
|
||||
### ARIA States
|
||||
|
||||
```tsx
|
||||
// aria-expanded for expandable elements
|
||||
<button
|
||||
aria-expanded={isOpen}
|
||||
aria-controls="dropdown-menu"
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
>
|
||||
Menu {isOpen ? <ChevronUp /> : <ChevronDown />}
|
||||
</button>
|
||||
<div id="dropdown-menu" hidden={!isOpen}>
|
||||
{/* Dropdown content */}
|
||||
</div>
|
||||
|
||||
// aria-pressed for toggle buttons
|
||||
<button
|
||||
aria-pressed={isPressed}
|
||||
onClick={() => setIsPressed(!isPressed)}
|
||||
>
|
||||
{isPressed ? 'Pressed' : 'Not Pressed'}
|
||||
</button>
|
||||
|
||||
// aria-selected for selectable items
|
||||
<div role="tab" aria-selected={isActive}>
|
||||
Tab 1
|
||||
</div>
|
||||
|
||||
// aria-checked for checkboxes/radio buttons
|
||||
<div
|
||||
role="checkbox"
|
||||
aria-checked={isChecked}
|
||||
tabIndex={0}
|
||||
onClick={() => setIsChecked(!isChecked)}
|
||||
>
|
||||
Custom Checkbox
|
||||
</div>
|
||||
```
|
||||
|
||||
### ARIA Live Regions
|
||||
|
||||
```tsx
|
||||
// Announce changes to screen readers
|
||||
<div
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
aria-atomic="true"
|
||||
>
|
||||
{statusMessage}
|
||||
</div>
|
||||
|
||||
// For urgent announcements
|
||||
<div
|
||||
role="alert"
|
||||
aria-live="assertive"
|
||||
aria-atomic="true"
|
||||
>
|
||||
{errorMessage}
|
||||
</div>
|
||||
|
||||
// For form validation
|
||||
<input
|
||||
type="email"
|
||||
aria-invalid={hasError}
|
||||
aria-describedby={hasError ? 'email-error' : undefined}
|
||||
/>
|
||||
{hasError && (
|
||||
<p id="email-error" role="alert">
|
||||
Please enter a valid email address
|
||||
</p>
|
||||
)}
|
||||
```
|
||||
|
||||
## Color Contrast
|
||||
|
||||
### Minimum Contrast Ratios (WCAG AA)
|
||||
|
||||
- **Normal text:** 4.5:1
|
||||
- **Large text (18pt+ or 14pt+ bold):** 3:1
|
||||
- **UI components and graphics:** 3:1
|
||||
|
||||
### Good Contrast Examples
|
||||
|
||||
```tsx
|
||||
// High contrast text
|
||||
<p className="text-slate-900 bg-white">
|
||||
Great contrast (21:1)
|
||||
</p>
|
||||
|
||||
<p className="text-slate-700 bg-white">
|
||||
Good contrast (8:1)
|
||||
</p>
|
||||
|
||||
// Button with good contrast
|
||||
<button className="
|
||||
bg-blue-600 text-white
|
||||
hover:bg-blue-700
|
||||
">
|
||||
High Contrast Button (4.5:1)
|
||||
</button>
|
||||
```
|
||||
|
||||
### Poor Contrast Examples (Avoid)
|
||||
|
||||
```tsx
|
||||
// ❌ Insufficient contrast
|
||||
<p className="text-gray-400 bg-white">
|
||||
Poor contrast (2.8:1) - fails WCAG AA
|
||||
</p>
|
||||
|
||||
// ❌ Don't rely on color alone
|
||||
<button className="bg-red-500 text-white">
|
||||
Error Button (color alone indicates state)
|
||||
</button>
|
||||
|
||||
// ✅ Better: Use icons + color
|
||||
<button className="bg-red-500 text-white flex items-center gap-2">
|
||||
<AlertCircle size={20} />
|
||||
Error: Fix Issues
|
||||
</button>
|
||||
```
|
||||
|
||||
### Tools for Checking Contrast
|
||||
|
||||
- Chrome DevTools: Inspect element → Accessibility tab
|
||||
- Online: WebAIM Contrast Checker
|
||||
- Figma: Stark plugin
|
||||
|
||||
## Alternative Text
|
||||
|
||||
### Images
|
||||
|
||||
```tsx
|
||||
// Informative images
|
||||
<img
|
||||
src="chart.png"
|
||||
alt="Bar chart showing sales increased 40% in Q4 2025"
|
||||
/>
|
||||
|
||||
// Decorative images
|
||||
<img
|
||||
src="decoration.png"
|
||||
alt=""
|
||||
role="presentation"
|
||||
/>
|
||||
|
||||
// Functional images (buttons)
|
||||
<button aria-label="Search">
|
||||
<img src="search-icon.png" alt="" />
|
||||
</button>
|
||||
|
||||
// Complex images
|
||||
<figure>
|
||||
<img
|
||||
src="complex-diagram.png"
|
||||
alt="System architecture diagram"
|
||||
aria-describedby="diagram-description"
|
||||
/>
|
||||
<figcaption id="diagram-description">
|
||||
Detailed description of the system architecture showing
|
||||
three main components: frontend, API layer, and database.
|
||||
The frontend communicates with the API via REST...
|
||||
</figcaption>
|
||||
</figure>
|
||||
```
|
||||
|
||||
### Icons
|
||||
|
||||
```tsx
|
||||
import { MagnifyingGlass, Bell, User } from '@phosphor-icons/react';
|
||||
|
||||
// Decorative icons (with adjacent text)
|
||||
<button className="flex items-center gap-2">
|
||||
<MagnifyingGlass aria-hidden="true" />
|
||||
Search
|
||||
</button>
|
||||
|
||||
// Functional icons (no adjacent text)
|
||||
<button aria-label="Search">
|
||||
<MagnifyingGlass />
|
||||
</button>
|
||||
|
||||
// Icons with state
|
||||
<button aria-label="Notifications (3 unread)">
|
||||
<Bell />
|
||||
<span className="sr-only">3 unread notifications</span>
|
||||
<span aria-hidden="true" className="badge">3</span>
|
||||
</button>
|
||||
```
|
||||
|
||||
## Forms
|
||||
|
||||
### Labels and Instructions
|
||||
|
||||
```tsx
|
||||
// Always associate labels with inputs
|
||||
<div>
|
||||
<label htmlFor="email" className="block mb-1 font-medium">
|
||||
Email Address
|
||||
</label>
|
||||
<input
|
||||
id="email"
|
||||
type="email"
|
||||
required
|
||||
aria-required="true"
|
||||
className="w-full px-4 py-2 border rounded-lg"
|
||||
/>
|
||||
</div>
|
||||
|
||||
// Group related inputs
|
||||
<fieldset>
|
||||
<legend className="font-medium mb-2">Contact Preferences</legend>
|
||||
<div className="space-y-2">
|
||||
<label className="flex items-center gap-2">
|
||||
<input type="checkbox" name="email" />
|
||||
Email
|
||||
</label>
|
||||
<label className="flex items-center gap-2">
|
||||
<input type="checkbox" name="sms" />
|
||||
SMS
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
```tsx
|
||||
<div>
|
||||
<label htmlFor="password" className="block mb-1 font-medium">
|
||||
Password
|
||||
</label>
|
||||
<input
|
||||
id="password"
|
||||
type="password"
|
||||
aria-invalid={hasError}
|
||||
aria-describedby="password-requirements password-error"
|
||||
className={`
|
||||
w-full px-4 py-2 border rounded-lg
|
||||
${hasError ? 'border-red-500' : 'border-slate-300'}
|
||||
`}
|
||||
/>
|
||||
<p id="password-requirements" className="text-sm text-slate-600 mt-1">
|
||||
Must be at least 8 characters
|
||||
</p>
|
||||
{hasError && (
|
||||
<p id="password-error" role="alert" className="text-sm text-red-600 mt-1">
|
||||
<AlertCircle className="inline" size={16} />
|
||||
Password is too short
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
```
|
||||
|
||||
### Required Fields
|
||||
|
||||
```tsx
|
||||
// Indicate required fields clearly
|
||||
<label htmlFor="name" className="block mb-1 font-medium">
|
||||
Full Name
|
||||
<span className="text-red-600" aria-label="required">*</span>
|
||||
</label>
|
||||
<input
|
||||
id="name"
|
||||
type="text"
|
||||
required
|
||||
aria-required="true"
|
||||
className="w-full px-4 py-2 border rounded-lg"
|
||||
/>
|
||||
|
||||
// Or use text
|
||||
<label htmlFor="email" className="block mb-1 font-medium">
|
||||
Email
|
||||
<span className="text-sm font-normal text-slate-600">(required)</span>
|
||||
</label>
|
||||
```
|
||||
|
||||
## Screen Reader-Only Content
|
||||
|
||||
### sr-only Class
|
||||
|
||||
```css
|
||||
/* Add to your CSS */
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
.focus\:not-sr-only:focus {
|
||||
position: static;
|
||||
width: auto;
|
||||
height: auto;
|
||||
padding: inherit;
|
||||
margin: inherit;
|
||||
overflow: visible;
|
||||
clip: auto;
|
||||
white-space: normal;
|
||||
}
|
||||
```
|
||||
|
||||
### Usage Examples
|
||||
|
||||
```tsx
|
||||
// Add context for screen readers
|
||||
<button>
|
||||
<Heart />
|
||||
<span className="sr-only">Add to favorites</span>
|
||||
</button>
|
||||
|
||||
// Provide additional context
|
||||
<div>
|
||||
<h2>Products</h2>
|
||||
<span className="sr-only">Showing 24 of 100 results</span>
|
||||
</div>
|
||||
|
||||
// Skip link
|
||||
<a href="#main" className="sr-only focus:not-sr-only">
|
||||
Skip to main content
|
||||
</a>
|
||||
```
|
||||
|
||||
## Focus Indicators
|
||||
|
||||
### Visible Focus States
|
||||
|
||||
```tsx
|
||||
// Default focus with ring
|
||||
<button className="
|
||||
px-4 py-2 rounded-lg
|
||||
bg-blue-600 text-white
|
||||
focus:outline-none
|
||||
focus:ring-4 focus:ring-blue-500
|
||||
focus:ring-offset-2
|
||||
">
|
||||
Click Me
|
||||
</button>
|
||||
|
||||
// Custom focus style
|
||||
<a
|
||||
href="/page"
|
||||
className="
|
||||
underline
|
||||
focus:outline-none
|
||||
focus:ring-2 focus:ring-blue-500
|
||||
focus:rounded
|
||||
"
|
||||
>
|
||||
Link Text
|
||||
</a>
|
||||
|
||||
// Focus within containers
|
||||
<div className="
|
||||
p-4 border border-slate-300 rounded-lg
|
||||
focus-within:ring-4 focus-within:ring-blue-500
|
||||
focus-within:border-blue-500
|
||||
">
|
||||
<input type="text" className="w-full focus:outline-none" />
|
||||
</div>
|
||||
```
|
||||
|
||||
### Focus Management in Modals
|
||||
|
||||
```tsx
|
||||
import { useEffect, useRef } from 'react';
|
||||
|
||||
function Modal({ isOpen, onClose, children }) {
|
||||
const modalRef = useRef(null);
|
||||
const previousFocus = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
// Store current focus
|
||||
previousFocus.current = document.activeElement;
|
||||
|
||||
// Focus modal
|
||||
modalRef.current?.focus();
|
||||
|
||||
// Trap focus within modal
|
||||
const handleTab = (e) => {
|
||||
if (e.key === 'Tab') {
|
||||
const focusableElements = modalRef.current.querySelectorAll(
|
||||
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
||||
);
|
||||
const firstElement = focusableElements[0];
|
||||
const lastElement = focusableElements[focusableElements.length - 1];
|
||||
|
||||
if (e.shiftKey && document.activeElement === firstElement) {
|
||||
e.preventDefault();
|
||||
lastElement.focus();
|
||||
} else if (!e.shiftKey && document.activeElement === lastElement) {
|
||||
e.preventDefault();
|
||||
firstElement.focus();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('keydown', handleTab);
|
||||
return () => document.removeEventListener('keydown', handleTab);
|
||||
} else {
|
||||
// Restore focus
|
||||
previousFocus.current?.focus();
|
||||
}
|
||||
}, [isOpen]);
|
||||
|
||||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="fixed inset-0 bg-black/50 flex items-center justify-center"
|
||||
onClick={onClose}
|
||||
>
|
||||
<div
|
||||
ref={modalRef}
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
tabIndex={-1}
|
||||
className="bg-white rounded-lg p-6 max-w-md"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{children}
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="mt-4 px-4 py-2 bg-slate-200 rounded-lg"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
### Automated Testing
|
||||
|
||||
```bash
|
||||
# Install axe-core for accessibility testing
|
||||
npm install --save-dev @axe-core/react
|
||||
|
||||
# Use in tests
|
||||
import { axe, toHaveNoViolations } from 'jest-axe';
|
||||
expect.extend(toHaveNoViolations);
|
||||
|
||||
test('should have no accessibility violations', async () => {
|
||||
const { container } = render(<MyComponent />);
|
||||
const results = await axe(container);
|
||||
expect(results).toHaveNoViolations();
|
||||
});
|
||||
```
|
||||
|
||||
### Manual Testing
|
||||
|
||||
**Keyboard Navigation:**
|
||||
- [ ] Can navigate entire site using Tab key
|
||||
- [ ] Can activate all interactive elements with Enter/Space
|
||||
- [ ] Focus indicators are clearly visible
|
||||
- [ ] No keyboard traps
|
||||
- [ ] Logical tab order
|
||||
|
||||
**Screen Reader Testing:**
|
||||
- [ ] Test with NVDA (Windows) or VoiceOver (Mac)
|
||||
- [ ] All images have appropriate alt text
|
||||
- [ ] Headings create logical structure
|
||||
- [ ] Forms have proper labels
|
||||
- [ ] Dynamic content is announced
|
||||
|
||||
**Visual Testing:**
|
||||
- [ ] Text has sufficient contrast (4.5:1 minimum)
|
||||
- [ ] UI works at 200% zoom
|
||||
- [ ] Content reflows properly on mobile
|
||||
- [ ] No information conveyed by color alone
|
||||
- [ ] Focus indicators are visible
|
||||
|
||||
**Tools to Use:**
|
||||
- Chrome DevTools Lighthouse
|
||||
- WAVE browser extension
|
||||
- axe DevTools browser extension
|
||||
- Color contrast analyzer
|
||||
- Screen reader (NVDA/VoiceOver)
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Accessible Modal
|
||||
|
||||
```tsx
|
||||
<div
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="modal-title"
|
||||
aria-describedby="modal-description"
|
||||
className="fixed inset-0 z-50 flex items-center justify-center"
|
||||
>
|
||||
<div className="fixed inset-0 bg-black/50" onClick={onClose} />
|
||||
<div className="relative bg-white rounded-lg p-6 max-w-md">
|
||||
<h2 id="modal-title" className="text-xl font-bold mb-2">
|
||||
Confirm Action
|
||||
</h2>
|
||||
<p id="modal-description" className="text-slate-600 mb-4">
|
||||
Are you sure you want to proceed?
|
||||
</p>
|
||||
<div className="flex gap-4">
|
||||
<button
|
||||
onClick={onConfirm}
|
||||
className="px-4 py-2 bg-blue-600 text-white rounded-lg"
|
||||
>
|
||||
Confirm
|
||||
</button>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="px-4 py-2 border border-slate-300 rounded-lg"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Accessible Tabs
|
||||
|
||||
```tsx
|
||||
function Tabs({ tabs }) {
|
||||
const [activeTab, setActiveTab] = useState(0);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div role="tablist" aria-label="Content sections">
|
||||
{tabs.map((tab, index) => (
|
||||
<button
|
||||
key={index}
|
||||
role="tab"
|
||||
aria-selected={activeTab === index}
|
||||
aria-controls={`panel-${index}`}
|
||||
id={`tab-${index}`}
|
||||
tabIndex={activeTab === index ? 0 : -1}
|
||||
onClick={() => setActiveTab(index)}
|
||||
className={`
|
||||
px-4 py-2 border-b-2
|
||||
${activeTab === index
|
||||
? 'border-blue-600 font-medium'
|
||||
: 'border-transparent'
|
||||
}
|
||||
`}
|
||||
>
|
||||
{tab.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
{tabs.map((tab, index) => (
|
||||
<div
|
||||
key={index}
|
||||
role="tabpanel"
|
||||
id={`panel-${index}`}
|
||||
aria-labelledby={`tab-${index}`}
|
||||
hidden={activeTab !== index}
|
||||
className="p-4"
|
||||
>
|
||||
{tab.content}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Accessible Tooltip
|
||||
|
||||
```tsx
|
||||
function Tooltip({ text, children }) {
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const tooltipId = useId();
|
||||
|
||||
return (
|
||||
<div className="relative inline-block">
|
||||
<button
|
||||
aria-describedby={isVisible ? tooltipId : undefined}
|
||||
onMouseEnter={() => setIsVisible(true)}
|
||||
onMouseLeave={() => setIsVisible(false)}
|
||||
onFocus={() => setIsVisible(true)}
|
||||
onBlur={() => setIsVisible(false)}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
{isVisible && (
|
||||
<div
|
||||
id={tooltipId}
|
||||
role="tooltip"
|
||||
className="
|
||||
absolute z-10 px-3 py-2
|
||||
bg-slate-900 text-white text-sm
|
||||
rounded-lg
|
||||
bottom-full left-1/2 -translate-x-1/2 mb-2
|
||||
"
|
||||
>
|
||||
{text}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- [WCAG 2.1 Quick Reference](https://www.w3.org/WAI/WCAG21/quickref/)
|
||||
- [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/)
|
||||
- [ARIA Authoring Practices Guide](https://www.w3.org/WAI/ARIA/apg/)
|
||||
- [axe DevTools](https://www.deque.com/axe/devtools/)
|
||||
- [WAVE Browser Extension](https://wave.webaim.org/extension/)
|
||||
577
skills/controlled-ux-designer/DESIGN-SYSTEM-TEMPLATE.md
Normal file
577
skills/controlled-ux-designer/DESIGN-SYSTEM-TEMPLATE.md
Normal file
@@ -0,0 +1,577 @@
|
||||
# Design System Template
|
||||
|
||||
Meta-framework for understanding what's fixed, project-specific, and adaptable in your design system.
|
||||
|
||||
## Purpose
|
||||
|
||||
This template helps you distinguish between:
|
||||
- **Fixed Elements**: Universal rules that never change
|
||||
- **Project-Specific Elements**: Filled in for each project based on brand
|
||||
- **Adaptable Elements**: Context-dependent implementations
|
||||
|
||||
---
|
||||
|
||||
## I. FIXED ELEMENTS
|
||||
|
||||
These foundations remain consistent across all projects, regardless of brand or context.
|
||||
|
||||
### 1. Spacing Scale
|
||||
|
||||
**Fixed System:**
|
||||
```
|
||||
4px, 8px, 12px, 16px, 24px, 32px, 48px, 64px, 96px
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
- Margins, padding, gaps between elements
|
||||
- Mathematical relationships ensure visual harmony
|
||||
- Use multipliers of base unit (4px)
|
||||
|
||||
**Why Fixed:**
|
||||
Consistent spacing creates visual rhythm regardless of brand personality.
|
||||
|
||||
### 2. Grid System
|
||||
|
||||
**Fixed Structure:**
|
||||
- **12-column grid** for most layouts (divisible by 2, 3, 4, 6)
|
||||
- **16-column grid** for data-heavy interfaces
|
||||
- **Gutters**: 16px (mobile), 24px (tablet), 32px (desktop)
|
||||
|
||||
**Why Fixed:**
|
||||
Grid provides structural order. Brand personality shows through color, typography, content—not grid structure.
|
||||
|
||||
### 3. Accessibility Standards
|
||||
|
||||
**Fixed Requirements:**
|
||||
- **WCAG 2.1 AA** compliance minimum
|
||||
- **Contrast**: 4.5:1 for normal text, 3:1 for large text
|
||||
- **Touch targets**: Minimum 44×44px
|
||||
- **Keyboard navigation**: All interactive elements accessible
|
||||
- **Screen reader**: Semantic HTML, ARIA labels where needed
|
||||
|
||||
**Why Fixed:**
|
||||
Accessibility is not negotiable. It's a baseline requirement for ethical, legal, and usable products.
|
||||
|
||||
### 4. Typography Hierarchy Logic
|
||||
|
||||
**Fixed Structure:**
|
||||
- **Mathematical scaling**: 1.25x (major third) or 1.333x (perfect fourth)
|
||||
- **Hierarchy levels**: Display → H1 → H2 → H3 → Body → Small → Caption
|
||||
- **Line height**: 1.5x for body text, 1.2-1.3x for headlines
|
||||
- **Line length**: 45-75 characters optimal
|
||||
|
||||
**Why Fixed:**
|
||||
Mathematical relationships create predictable, harmonious hierarchy. Specific fonts change, but the logic doesn't.
|
||||
|
||||
### 5. Component Architecture
|
||||
|
||||
**Fixed Patterns:**
|
||||
- **Button states**: Default, Hover, Active, Focus, Disabled
|
||||
- **Form structure**: Label above input, error below, helper text optional
|
||||
- **Modal pattern**: Overlay + centered content + close mechanism
|
||||
- **Card structure**: Container → Header → Body → Footer (optional)
|
||||
|
||||
**Why Fixed:**
|
||||
Users expect consistent component behavior. Architecture is fixed; appearance is project-specific.
|
||||
|
||||
### 6. Animation Timing Framework
|
||||
|
||||
**Fixed Physics Profiles:**
|
||||
- **Lightweight** (icons, chips): 150ms
|
||||
- **Standard** (cards, panels): 300ms
|
||||
- **Weighty** (modals, pages): 500ms
|
||||
|
||||
**Fixed Easing:**
|
||||
- **Ease-out**: Entrances (fast start, slow end)
|
||||
- **Ease-in**: Exits (slow start, fast end)
|
||||
- **Ease-in-out**: Transitions (smooth both ends)
|
||||
|
||||
**Why Fixed:**
|
||||
Natural physics feel consistent across brands. Duration and easing create that feeling.
|
||||
|
||||
---
|
||||
|
||||
## II. PROJECT-SPECIFIC ELEMENTS
|
||||
|
||||
Fill in these for each project based on brand personality and purpose.
|
||||
|
||||
### 1. Brand Color System
|
||||
|
||||
**Template Structure:**
|
||||
|
||||
```
|
||||
NEUTRALS (4-5 colors):
|
||||
- Background lightest: _______ (e.g., slate-50 or warm-white)
|
||||
- Surface: _______ (e.g., slate-100)
|
||||
- Border/divider: _______ (e.g., slate-300)
|
||||
- Text secondary: _______ (e.g., slate-600)
|
||||
- Text primary: _______ (e.g., slate-900)
|
||||
|
||||
ACCENTS (1-3 colors):
|
||||
- Primary (main CTA): _______ (e.g., teal-500)
|
||||
- Secondary (alternative action): _______ (optional)
|
||||
- Status colors:
|
||||
- Success: _______ (green-ish)
|
||||
- Warning: _______ (amber-ish)
|
||||
- Error: _______ (red-ish)
|
||||
- Info: _______ (blue-ish)
|
||||
```
|
||||
|
||||
**Questions to Answer:**
|
||||
- What emotion should the brand evoke? (Trust, excitement, calm, urgency)
|
||||
- Warm or cool neutrals?
|
||||
- Conservative or bold accents?
|
||||
|
||||
**Examples:**
|
||||
|
||||
**Project A: Fintech App**
|
||||
```
|
||||
Neutrals: Cool greys (slate-50 → slate-900)
|
||||
Primary: Deep blue (#0A2463) – trust, professionalism
|
||||
Success: Muted green (#10B981)
|
||||
Why: Financial products need trust, not playfulness
|
||||
```
|
||||
|
||||
**Project B: Creative Community**
|
||||
```
|
||||
Neutrals: Warm greys with beige undertones
|
||||
Primary: Coral (#FF6B6B) – energy, creativity
|
||||
Success: Teal (#06D6A0) – fresh, unexpected
|
||||
Why: Creative spaces should feel inviting, not corporate
|
||||
```
|
||||
|
||||
**Project C: Healthcare Platform**
|
||||
```
|
||||
Neutrals: Pure greys (minimal color temperature)
|
||||
Primary: Soft blue (#4A90E2) – calm, clinical
|
||||
Success: Medical green (#38A169)
|
||||
Why: Healthcare needs clarity and calm, not distraction
|
||||
```
|
||||
|
||||
### 2. Typography Pairing
|
||||
|
||||
**Template:**
|
||||
|
||||
```
|
||||
HEADLINE FONT: _______
|
||||
- Weight: _______ (e.g., Bold 700)
|
||||
- Use case: H1, H2, display text
|
||||
- Personality: _______ (geometric/humanist/serif/etc.)
|
||||
|
||||
BODY FONT: _______
|
||||
- Weight: _______ (e.g., Regular 400, Medium 500)
|
||||
- Use case: Paragraphs, UI text
|
||||
- Personality: _______ (neutral/readable/efficient)
|
||||
|
||||
OPTIONAL ACCENT FONT: _______
|
||||
- Weight: _______
|
||||
- Use case: _______ (special headlines, callouts)
|
||||
```
|
||||
|
||||
**Pairing Logic:**
|
||||
- Serif + Sans-serif (classic, editorial)
|
||||
- Geometric + Humanist (modern + warm)
|
||||
- Display + System (distinctive + efficient)
|
||||
|
||||
**Examples:**
|
||||
|
||||
**Project A: Editorial Platform**
|
||||
```
|
||||
Headline: Playfair Display (Serif, Bold 700)
|
||||
Body: Inter (Sans-serif, Regular 400)
|
||||
Why: Serif headlines = trustworthy, editorial feel
|
||||
```
|
||||
|
||||
**Project B: Tech Startup**
|
||||
```
|
||||
Headline: DM Sans (Sans-serif, Bold 700)
|
||||
Body: DM Sans (Regular 400, Medium 500)
|
||||
Why: Single-font system = modern, efficient, cohesive
|
||||
```
|
||||
|
||||
**Project C: Luxury Brand**
|
||||
```
|
||||
Headline: Cormorant Garamond (Serif, Light 300)
|
||||
Body: Lato (Sans-serif, Regular 400)
|
||||
Why: Elegant serif + readable sans = sophisticated
|
||||
```
|
||||
|
||||
### 3. Tone of Voice
|
||||
|
||||
**Template:**
|
||||
|
||||
```
|
||||
BRAND PERSONALITY:
|
||||
- Formal ↔ Casual: _______ (1-10 scale)
|
||||
- Professional ↔ Friendly: _______ (1-10 scale)
|
||||
- Serious ↔ Playful: _______ (1-10 scale)
|
||||
- Authoritative ↔ Conversational: _______ (1-10 scale)
|
||||
|
||||
MICROCOPY EXAMPLES:
|
||||
- Button label (submit form): _______
|
||||
- Error message (invalid email): _______
|
||||
- Success message (saved): _______
|
||||
- Empty state: _______
|
||||
|
||||
ANIMATION PERSONALITY:
|
||||
- Speed: _______ (quick/moderate/slow)
|
||||
- Feel: _______ (precise/smooth/bouncy)
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
|
||||
**Project A: Banking App**
|
||||
```
|
||||
Personality: Formal (8), Professional (9), Serious (8)
|
||||
Button: "Submit Application"
|
||||
Error: "Email address format is invalid"
|
||||
Success: "Application submitted successfully"
|
||||
Animation: Quick (precise, efficient, no-nonsense)
|
||||
```
|
||||
|
||||
**Project B: Social App**
|
||||
```
|
||||
Personality: Casual (8), Friendly (9), Playful (7)
|
||||
Button: "Let's go!"
|
||||
Error: "Hmm, that email doesn't look right"
|
||||
Success: "Nice! You're all set 🎉"
|
||||
Animation: Moderate (smooth, friendly bounce)
|
||||
```
|
||||
|
||||
### 4. Animation Speed & Feel
|
||||
|
||||
**Template:**
|
||||
|
||||
```
|
||||
SPEED PREFERENCE:
|
||||
- UI interactions: _______ (100-150ms / 150-200ms / 200-300ms)
|
||||
- State changes: _______ (200ms / 300ms / 400ms)
|
||||
- Page transitions: _______ (300ms / 500ms / 700ms)
|
||||
|
||||
ANIMATION STYLE:
|
||||
- Easing preference: _______ (sharp / standard / bouncy)
|
||||
- Movement type: _______ (minimal / smooth / expressive)
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
|
||||
**Project A: Trading Platform**
|
||||
```
|
||||
Speed: Fast (100ms UI, 200ms states, 300ms pages)
|
||||
Style: Sharp easing, minimal movement
|
||||
Why: Traders need speed, not distraction
|
||||
```
|
||||
|
||||
**Project B: Wellness App**
|
||||
```
|
||||
Speed: Slow (200ms UI, 400ms states, 500ms pages)
|
||||
Style: Smooth easing, gentle movement
|
||||
Why: Calm, relaxing experience matches brand
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## III. ADAPTABLE ELEMENTS
|
||||
|
||||
Context-dependent implementations that vary based on use case.
|
||||
|
||||
### 1. Component Variations
|
||||
|
||||
**Button Variants:**
|
||||
- **Primary**: Full background color (high emphasis)
|
||||
- **Secondary**: Outline only (medium emphasis)
|
||||
- **Tertiary**: Text only (low emphasis)
|
||||
- **Destructive**: Red-ish (danger actions)
|
||||
- **Ghost**: Minimal (navigation, toolbars)
|
||||
|
||||
**Adaptation Rules:**
|
||||
- Primary: Main CTA, one per screen section
|
||||
- Secondary: Alternative actions
|
||||
- Tertiary: Less important actions, multiple allowed
|
||||
- Use brand colors, but hierarchy logic is fixed
|
||||
|
||||
### 2. Responsive Breakpoints
|
||||
|
||||
**Fixed Ranges:**
|
||||
- XS: 0-479px (small phones)
|
||||
- SM: 480-767px (large phones)
|
||||
- MD: 768-1023px (tablets)
|
||||
- LG: 1024-1439px (laptops)
|
||||
- XL: 1440px+ (desktop)
|
||||
|
||||
**Adaptable Implementations:**
|
||||
|
||||
**Simple Content Site:**
|
||||
```
|
||||
XS-SM: Single column
|
||||
MD: 2 columns
|
||||
LG-XL: 3 columns max
|
||||
Why: Content-focused, don't overwhelm
|
||||
```
|
||||
|
||||
**Dashboard/Data App:**
|
||||
```
|
||||
XS: Collapsed, cards stack
|
||||
SM: Simplified sidebar
|
||||
MD: Full sidebar + main content
|
||||
LG-XL: Sidebar + main + right panel
|
||||
Why: Data apps need more screen real estate
|
||||
```
|
||||
|
||||
### 3. Dark Mode Palette
|
||||
|
||||
**Adaptation Strategy:**
|
||||
|
||||
Not a simple inversion. Dark mode needs adjusted contrast:
|
||||
|
||||
**Light Mode:**
|
||||
```
|
||||
Background: #FFFFFF (white)
|
||||
Text: #0F172A (slate-900) → 21:1 contrast
|
||||
```
|
||||
|
||||
**Dark Mode (Adapted):**
|
||||
```
|
||||
Background: #0F172A (slate-900)
|
||||
Text: #E2E8F0 (slate-200) → 15.8:1 contrast (still AA, but softer)
|
||||
```
|
||||
|
||||
**Why Adapt:**
|
||||
Pure white on pure black is too harsh. Dark mode needs slightly lower contrast for eye comfort.
|
||||
|
||||
### 4. Loading States
|
||||
|
||||
**Context-Dependent:**
|
||||
|
||||
**Fast operations (<500ms):**
|
||||
- No loading indicator (feels instant)
|
||||
|
||||
**Medium operations (500ms-2s):**
|
||||
- Spinner or skeleton screen
|
||||
|
||||
**Long operations (>2s):**
|
||||
- Progress bar with percentage
|
||||
- Or: Skeleton + estimated time
|
||||
|
||||
**Interactive Operations:**
|
||||
- Button shows spinner inside (don't disable, show state)
|
||||
|
||||
### 5. Error Handling Strategy
|
||||
|
||||
**Context-Dependent:**
|
||||
|
||||
**Form Errors:**
|
||||
```
|
||||
Validate: On blur (after user leaves field)
|
||||
Display: Inline below field
|
||||
Recovery: Clear error on fix
|
||||
```
|
||||
|
||||
**API Errors:**
|
||||
```
|
||||
Transient (network): Show retry button
|
||||
Permanent (404): Show helpful message + next steps
|
||||
Critical (500): Contact support option
|
||||
```
|
||||
|
||||
**Data Errors:**
|
||||
```
|
||||
Missing: Show empty state with action
|
||||
Corrupt: Show error boundary with reload
|
||||
Invalid: Highlight + explain what's wrong
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## DECISION TREE
|
||||
|
||||
When implementing a feature, ask:
|
||||
|
||||
### Is this...
|
||||
|
||||
**FIXED?**
|
||||
- Does it affect structure, accessibility, or universal UX?
|
||||
- Examples: Spacing scale, grid, contrast ratios, component architecture
|
||||
- **Action**: Use the fixed system, no variation
|
||||
|
||||
**PROJECT-SPECIFIC?**
|
||||
- Does it express brand personality or purpose?
|
||||
- Examples: Colors, typography, tone of voice, animation feel
|
||||
- **Action**: Fill in the template for this project
|
||||
|
||||
**ADAPTABLE?**
|
||||
- Does it depend on context, content, or use case?
|
||||
- Examples: Component variants, responsive behavior, error handling
|
||||
- **Action**: Choose appropriate variation based on context
|
||||
|
||||
---
|
||||
|
||||
## EXAMPLE: Implementing a "Submit" Button
|
||||
|
||||
### Fixed Elements (Always the same):
|
||||
- Touch target: 44px minimum height
|
||||
- Padding: 16px horizontal (from spacing scale)
|
||||
- States: Default, Hover, Active, Focus, Disabled
|
||||
- Animation: 150ms ease-out (lightweight profile)
|
||||
|
||||
### Project-Specific (Filled per project):
|
||||
- **Project A (Bank)**: Dark blue background, white text, "Submit Application"
|
||||
- **Project B (Social)**: Coral background, white text, "Let's Go!"
|
||||
- **Project C (Healthcare)**: Soft blue background, white text, "Continue"
|
||||
|
||||
### Adaptable (Context-dependent):
|
||||
- **Form context**: Primary button (full color)
|
||||
- **Toolbar context**: Ghost button (text only)
|
||||
- **Danger context**: Destructive variant (red-ish)
|
||||
|
||||
---
|
||||
|
||||
## VALIDATION CHECKLIST
|
||||
|
||||
Before finalizing a design, check:
|
||||
|
||||
### Fixed Elements
|
||||
- [ ] Uses spacing scale (4/8/12/16/24/32/48/64/96px)
|
||||
- [ ] Follows grid system (12 or 16 columns)
|
||||
- [ ] Meets WCAG AA contrast (4.5:1 normal, 3:1 large)
|
||||
- [ ] Touch targets ≥ 44px
|
||||
- [ ] Typography follows mathematical scale
|
||||
- [ ] Components follow standard architecture
|
||||
|
||||
### Project-Specific Elements
|
||||
- [ ] Brand colors filled in and intentional
|
||||
- [ ] Typography pairing chosen and justified
|
||||
- [ ] Tone of voice defined and consistent
|
||||
- [ ] Animation speed matches brand personality
|
||||
|
||||
### Adaptable Elements
|
||||
- [ ] Component variants appropriate for context
|
||||
- [ ] Responsive behavior fits content type
|
||||
- [ ] Loading states match operation duration
|
||||
- [ ] Error handling fits error type
|
||||
|
||||
---
|
||||
|
||||
## PROJECT KICKOFF TEMPLATE
|
||||
|
||||
Use this to start a new project:
|
||||
|
||||
```
|
||||
PROJECT NAME: _______________________
|
||||
PURPOSE: ____________________________
|
||||
|
||||
BRAND PERSONALITY:
|
||||
- Primary emotion: _______
|
||||
- Warm or cool: _______
|
||||
- Formal or casual: _______
|
||||
- Conservative or bold: _______
|
||||
|
||||
COLORS (fill the template):
|
||||
- Neutral base: _______
|
||||
- Primary accent: _______
|
||||
- Status colors: _______ / _______ / _______
|
||||
|
||||
TYPOGRAPHY (fill the template):
|
||||
- Headline font: _______
|
||||
- Body font: _______
|
||||
- Pairing rationale: _______
|
||||
|
||||
TONE:
|
||||
- Button labels style: _______
|
||||
- Error message style: _______
|
||||
- Success message style: _______
|
||||
|
||||
ANIMATION:
|
||||
- Speed preference: _______ (fast/moderate/slow)
|
||||
- Feel preference: _______ (sharp/smooth/bouncy)
|
||||
|
||||
TARGET DEVICES:
|
||||
- Primary: _______ (mobile/desktop/both)
|
||||
- Secondary: _______
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## MAINTAINING CONSISTENCY
|
||||
|
||||
### Documentation
|
||||
- Keep this template updated as system evolves
|
||||
- Document WHY choices were made, not just WHAT
|
||||
|
||||
### Communication
|
||||
- Share with designers: "Here's what varies vs. what's fixed"
|
||||
- Share with developers: "Here are the design tokens"
|
||||
|
||||
### Tooling
|
||||
- Use CSS variables for project-specific values
|
||||
- Use Tailwind config for spacing scale
|
||||
- Use design tokens in Figma/Storybook
|
||||
|
||||
### Reviews
|
||||
- Audit: Does new work follow fixed elements?
|
||||
- Validate: Are project-specific elements intentional?
|
||||
- Question: Are adaptations justified by context?
|
||||
|
||||
---
|
||||
|
||||
## EXAMPLES OF COMPLETE SYSTEMS
|
||||
|
||||
### System A: B2B SaaS (Conservative)
|
||||
|
||||
**Fixed**: Standard spacing, 12-col grid, WCAG AA, major third type scale
|
||||
**Project-Specific**:
|
||||
- Colors: Cool greys + corporate blue
|
||||
- Typography: DM Sans (headlines + body)
|
||||
- Tone: Professional, formal
|
||||
- Animation: Quick, precise (150ms)
|
||||
**Adaptable**:
|
||||
- Dashboard gets multi-panel layout
|
||||
- Forms are extensive (use progressive disclosure)
|
||||
- Errors show detailed technical info
|
||||
|
||||
### System B: Consumer Social App (Playful)
|
||||
|
||||
**Fixed**: Same spacing/grid/accessibility/type logic
|
||||
**Project-Specific**:
|
||||
- Colors: Warm greys + vibrant coral
|
||||
- Typography: Poppins (headlines) + Inter (body)
|
||||
- Tone: Casual, friendly, playful
|
||||
- Animation: Moderate, bouncy (200ms)
|
||||
**Adaptable**:
|
||||
- Mobile-first (most users on phones)
|
||||
- Forms are minimal (progressive profiling)
|
||||
- Errors are friendly, not technical
|
||||
|
||||
### System C: Healthcare Platform (Clinical)
|
||||
|
||||
**Fixed**: Same foundational structure
|
||||
**Project-Specific**:
|
||||
- Colors: Pure greys + medical blue
|
||||
- Typography: System fonts (SF Pro / Segoe)
|
||||
- Tone: Clear, authoritative, calm
|
||||
- Animation: Slow, smooth (300ms)
|
||||
**Adaptable**:
|
||||
- Desktop-first (clinical use at workstations)
|
||||
- Forms are complex (HIPAA compliance)
|
||||
- Errors are precise with next steps
|
||||
|
||||
---
|
||||
|
||||
## KEY TAKEAWAY
|
||||
|
||||
**The system flexibility framework lets you:**
|
||||
- Maintain consistency (fixed elements)
|
||||
- Express brand personality (project-specific)
|
||||
- Adapt to context (adaptable elements)
|
||||
|
||||
**Without this framework:**
|
||||
- Designers reinvent spacing every project
|
||||
- Components feel inconsistent across products
|
||||
- Brand personality overrides accessibility
|
||||
- Context-blind implementations feel wrong
|
||||
|
||||
**With this framework:**
|
||||
- Speed: Start from proven foundations
|
||||
- Consistency: Fixed elements guarantee it
|
||||
- Flexibility: Express unique brand identity
|
||||
- Context: Adapt without breaking system
|
||||
544
skills/controlled-ux-designer/MOTION-SPEC.md
Normal file
544
skills/controlled-ux-designer/MOTION-SPEC.md
Normal file
@@ -0,0 +1,544 @@
|
||||
# Motion Specification Template
|
||||
|
||||
Detailed animation specifications for consistent motion design across projects.
|
||||
|
||||
## Easing Curves
|
||||
|
||||
### Standard Easings
|
||||
|
||||
**Ease-out (Entrances)**
|
||||
```css
|
||||
cubic-bezier(0.0, 0.0, 0.2, 1)
|
||||
```
|
||||
Use for: Elements entering view, expanding, appearing
|
||||
|
||||
**Ease-in (Exits)**
|
||||
```css
|
||||
cubic-bezier(0.4, 0.0, 1, 1)
|
||||
```
|
||||
Use for: Elements leaving view, collapsing, disappearing
|
||||
|
||||
**Ease-in-out (Transitions)**
|
||||
```css
|
||||
cubic-bezier(0.4, 0.0, 0.2, 1)
|
||||
```
|
||||
Use for: State changes, transformations, element swaps
|
||||
|
||||
**Linear (Continuous)**
|
||||
```css
|
||||
linear
|
||||
```
|
||||
Use for: Loading spinners, continuous animations, marquee scrolls
|
||||
|
||||
### Custom Easings
|
||||
|
||||
**Spring (Bouncy)**
|
||||
```css
|
||||
cubic-bezier(0.68, -0.55, 0.265, 1.55)
|
||||
```
|
||||
Use for: Playful interactions, game-like UIs, attention-grabbing
|
||||
|
||||
**Sharp (Quick snap)**
|
||||
```css
|
||||
cubic-bezier(0.4, 0.0, 0.6, 1)
|
||||
```
|
||||
Use for: Mechanical interactions, precise movements
|
||||
|
||||
## Duration Tables
|
||||
|
||||
### By Interaction Type
|
||||
|
||||
| Interaction | Duration | Easing | Example |
|
||||
|-------------|----------|--------|---------|
|
||||
| Button press | 100ms | ease-out | Background color change |
|
||||
| Hover state | 150ms | ease-out | Underline appearing |
|
||||
| Checkbox toggle | 150ms | ease-out | Checkmark animation |
|
||||
| Tooltip appear | 200ms | ease-out | Tooltip fade in |
|
||||
| Tab switch | 250ms | ease-in-out | Content swap |
|
||||
| Accordion expand | 300ms | ease-out | Height animation |
|
||||
| Modal open | 300ms | ease-out | Fade + scale up |
|
||||
| Modal close | 250ms | ease-in | Fade + scale down |
|
||||
| Page transition | 400ms | ease-in-out | Route change |
|
||||
| Sheet slide-in | 300ms | ease-out | Bottom sheet |
|
||||
| Toast notification | 300ms | ease-out | Slide in from top |
|
||||
|
||||
### By Element Weight
|
||||
|
||||
| Element Weight | Duration | Example |
|
||||
|----------------|----------|---------|
|
||||
| Lightweight (< 100px) | 150ms | Icons, badges, chips |
|
||||
| Standard (100-500px) | 300ms | Cards, panels, list items |
|
||||
| Weighty (> 500px) | 500ms | Modals, full-page transitions |
|
||||
|
||||
## State-Specific Animations
|
||||
|
||||
### Hover States
|
||||
|
||||
**Button Hover:**
|
||||
```tsx
|
||||
// Tailwind
|
||||
<button className="
|
||||
bg-blue-600 hover:bg-blue-700
|
||||
transition-colors duration-150 ease-out
|
||||
">
|
||||
Hover Me
|
||||
</button>
|
||||
|
||||
// Framer Motion
|
||||
<motion.button
|
||||
whileHover={{ scale: 1.02 }}
|
||||
transition={{ duration: 0.15, ease: "easeOut" }}
|
||||
>
|
||||
Hover Me
|
||||
</motion.button>
|
||||
```
|
||||
|
||||
**Link Hover:**
|
||||
```tsx
|
||||
<a className="
|
||||
underline-offset-4
|
||||
hover:underline
|
||||
transition-all duration-200 ease-out
|
||||
">
|
||||
Link Text
|
||||
</a>
|
||||
```
|
||||
|
||||
**Card Hover:**
|
||||
```tsx
|
||||
<div className="
|
||||
transition-all duration-200 ease-out
|
||||
hover:shadow-lg
|
||||
hover:scale-[1.02]
|
||||
">
|
||||
Card Content
|
||||
</div>
|
||||
```
|
||||
|
||||
### Focus States
|
||||
|
||||
**Keyboard Focus:**
|
||||
```tsx
|
||||
<button className="
|
||||
focus:outline-none
|
||||
focus:ring-4 focus:ring-blue-500
|
||||
focus:ring-offset-2
|
||||
transition-all duration-200 ease-out
|
||||
">
|
||||
Focus Me
|
||||
</button>
|
||||
```
|
||||
|
||||
**Input Focus:**
|
||||
```tsx
|
||||
<input className="
|
||||
border-2 border-slate-300
|
||||
focus:border-blue-500
|
||||
focus:ring-4 focus:ring-blue-200
|
||||
transition-all duration-200 ease-out
|
||||
" />
|
||||
```
|
||||
|
||||
### Active/Pressed States
|
||||
|
||||
**Button Press:**
|
||||
```tsx
|
||||
<motion.button
|
||||
whileTap={{ scale: 0.98 }}
|
||||
transition={{ duration: 0.1, ease: "easeIn" }}
|
||||
>
|
||||
Press Me
|
||||
</motion.button>
|
||||
|
||||
// CSS alternative
|
||||
<button className="
|
||||
active:scale-98
|
||||
transition-transform duration-100 ease-in
|
||||
">
|
||||
Press Me
|
||||
</button>
|
||||
```
|
||||
|
||||
### Disabled States
|
||||
|
||||
**Disabled Button:**
|
||||
```tsx
|
||||
<button
|
||||
disabled
|
||||
className="
|
||||
bg-slate-400 text-slate-600
|
||||
opacity-50 cursor-not-allowed
|
||||
pointer-events-none
|
||||
"
|
||||
>
|
||||
Disabled
|
||||
</button>
|
||||
```
|
||||
|
||||
### Loading States
|
||||
|
||||
**Loading Spinner:**
|
||||
```tsx
|
||||
<div className="
|
||||
w-8 h-8 border-4 border-slate-300
|
||||
border-t-blue-600 rounded-full
|
||||
animate-spin
|
||||
">
|
||||
</div>
|
||||
|
||||
// CSS
|
||||
@keyframes spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.spinner {
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
```
|
||||
|
||||
**Skeleton Loader:**
|
||||
```tsx
|
||||
<div className="animate-pulse space-y-4">
|
||||
<div className="h-4 bg-slate-200 rounded w-3/4"></div>
|
||||
<div className="h-4 bg-slate-200 rounded w-1/2"></div>
|
||||
</div>
|
||||
|
||||
// CSS
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.5; }
|
||||
}
|
||||
|
||||
.animate-pulse {
|
||||
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||
}
|
||||
```
|
||||
|
||||
### Success Feedback
|
||||
|
||||
**Checkmark Animation:**
|
||||
```tsx
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.5 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ duration: 0.3, ease: "easeOut" }}
|
||||
>
|
||||
<CheckCircle className="text-green-600" size={48} />
|
||||
</motion.div>
|
||||
```
|
||||
|
||||
**Toast Notification:**
|
||||
```tsx
|
||||
<motion.div
|
||||
initial={{ y: -100, opacity: 0 }}
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
exit={{ y: -100, opacity: 0 }}
|
||||
transition={{ duration: 0.3, ease: "easeOut" }}
|
||||
className="bg-green-600 text-white p-4 rounded-lg"
|
||||
>
|
||||
Success! Changes saved.
|
||||
</motion.div>
|
||||
```
|
||||
|
||||
### Error Feedback
|
||||
|
||||
**Shake Animation:**
|
||||
```tsx
|
||||
<motion.div
|
||||
animate={{ x: [0, -4, 4, -4, 4, 0] }}
|
||||
transition={{ duration: 0.3, ease: "easeInOut" }}
|
||||
className="border-2 border-red-500"
|
||||
>
|
||||
<input type="text" />
|
||||
</motion.div>
|
||||
|
||||
// CSS alternative
|
||||
@keyframes shake {
|
||||
0%, 100% { transform: translateX(0); }
|
||||
20%, 60% { transform: translateX(-4px); }
|
||||
40%, 80% { transform: translateX(4px); }
|
||||
}
|
||||
|
||||
.shake {
|
||||
animation: shake 0.3s ease-in-out;
|
||||
}
|
||||
```
|
||||
|
||||
**Error Message Slide-in:**
|
||||
```tsx
|
||||
<motion.div
|
||||
initial={{ height: 0, opacity: 0 }}
|
||||
animate={{ height: "auto", opacity: 1 }}
|
||||
exit={{ height: 0, opacity: 0 }}
|
||||
transition={{ duration: 0.2, ease: "easeOut" }}
|
||||
className="text-red-600 text-sm"
|
||||
>
|
||||
Please enter a valid email address
|
||||
</motion.div>
|
||||
```
|
||||
|
||||
### Warning Feedback
|
||||
|
||||
**Pulse Animation:**
|
||||
```tsx
|
||||
<motion.div
|
||||
animate={{ scale: [1, 1.05, 1] }}
|
||||
transition={{
|
||||
duration: 0.6,
|
||||
ease: "easeInOut",
|
||||
repeat: Infinity
|
||||
}}
|
||||
className="border-2 border-amber-500"
|
||||
>
|
||||
Warning Content
|
||||
</motion.div>
|
||||
```
|
||||
|
||||
### Form Validation
|
||||
|
||||
**Field Validation (On Blur):**
|
||||
```tsx
|
||||
// Validate on blur, not during typing
|
||||
<input
|
||||
onBlur={(e) => {
|
||||
const isValid = validateEmail(e.target.value);
|
||||
setError(!isValid);
|
||||
}}
|
||||
className={`
|
||||
border-2 transition-all duration-200 ease-out
|
||||
${error
|
||||
? 'border-red-500 focus:ring-red-200'
|
||||
: 'border-slate-300 focus:ring-blue-200'
|
||||
}
|
||||
`}
|
||||
/>
|
||||
|
||||
{error && (
|
||||
<motion.p
|
||||
initial={{ opacity: 0, y: -10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
className="text-red-600 text-sm mt-1"
|
||||
>
|
||||
Please enter a valid email
|
||||
</motion.p>
|
||||
)}
|
||||
```
|
||||
|
||||
## Common Animation Patterns
|
||||
|
||||
### Fade In
|
||||
```tsx
|
||||
// Framer Motion
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.3, ease: "easeOut" }}
|
||||
>
|
||||
Content
|
||||
</motion.div>
|
||||
|
||||
// CSS
|
||||
.fade-in {
|
||||
animation: fadeIn 0.3s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
```
|
||||
|
||||
### Slide Up
|
||||
```tsx
|
||||
// Framer Motion
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.3, ease: "easeOut" }}
|
||||
>
|
||||
Content
|
||||
</motion.div>
|
||||
|
||||
// CSS
|
||||
.slide-up {
|
||||
animation: slideUp 0.3s ease-out;
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Scale + Fade (Modal)
|
||||
```tsx
|
||||
// Framer Motion
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.95 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
exit={{ opacity: 0, scale: 0.95 }}
|
||||
transition={{ duration: 0.3, ease: "easeOut" }}
|
||||
>
|
||||
Modal content
|
||||
</motion.div>
|
||||
```
|
||||
|
||||
### Stagger Children
|
||||
```tsx
|
||||
// Framer Motion
|
||||
<motion.ul
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
variants={{
|
||||
visible: {
|
||||
transition: {
|
||||
staggerChildren: 0.1
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
{items.map(item => (
|
||||
<motion.li
|
||||
key={item.id}
|
||||
variants={{
|
||||
hidden: { opacity: 0, x: -20 },
|
||||
visible: { opacity: 1, x: 0 }
|
||||
}}
|
||||
>
|
||||
{item.name}
|
||||
</motion.li>
|
||||
))}
|
||||
</motion.ul>
|
||||
```
|
||||
|
||||
## Performance Checklist
|
||||
|
||||
- [ ] Only animate `transform` and `opacity`
|
||||
- [ ] Avoid animating `width`, `height`, `top`, `left`, `margin`, `padding`
|
||||
- [ ] Test on mobile devices (target 60fps)
|
||||
- [ ] Use `will-change` only for complex animations
|
||||
- [ ] Implement `prefers-reduced-motion` media query
|
||||
- [ ] Keep animation duration under 500ms for UI interactions
|
||||
- [ ] Use CSS animations for simple transitions (better performance)
|
||||
- [ ] Use JS animation libraries for complex, choreographed sequences
|
||||
|
||||
## Accessibility
|
||||
|
||||
```css
|
||||
/* Disable or reduce animations for users who prefer less motion */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Implementation in React:**
|
||||
```tsx
|
||||
import { useReducedMotion } from 'framer-motion';
|
||||
|
||||
function MyComponent() {
|
||||
const shouldReduceMotion = useReducedMotion();
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{
|
||||
duration: shouldReduceMotion ? 0.01 : 0.3,
|
||||
ease: "easeOut"
|
||||
}}
|
||||
>
|
||||
Content
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Testing Animations
|
||||
|
||||
1. **Test at 60fps** on target devices
|
||||
2. **Test with slow network** (does page still feel responsive?)
|
||||
3. **Test with reduced motion** preferences enabled
|
||||
4. **Verify animations don't block** critical user actions
|
||||
5. **Check that animations add value** (remove if purely decorative)
|
||||
6. **Test on low-end devices** (not just your development machine)
|
||||
7. **Measure performance** with Chrome DevTools Performance tab
|
||||
8. **Check for layout thrashing** (avoid reading and writing to DOM in same frame)
|
||||
|
||||
## Animation & Gestalt Principles
|
||||
|
||||
### Proximity
|
||||
Animated elements that are near each other should move together to reinforce grouping:
|
||||
```tsx
|
||||
// Animate card and its children together
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
>
|
||||
<h3>Title</h3>
|
||||
<p>Content</p>
|
||||
<button>Action</button>
|
||||
</motion.div>
|
||||
```
|
||||
|
||||
### Similarity
|
||||
Similar elements should have similar animation characteristics:
|
||||
```tsx
|
||||
// All buttons use same hover animation
|
||||
const buttonAnimation = {
|
||||
whileHover: { scale: 1.02 },
|
||||
transition: { duration: 0.15, ease: "easeOut" }
|
||||
};
|
||||
|
||||
<motion.button {...buttonAnimation}>Button 1</motion.button>
|
||||
<motion.button {...buttonAnimation}>Button 2</motion.button>
|
||||
```
|
||||
|
||||
### Continuity
|
||||
Movement should follow natural, smooth paths:
|
||||
```tsx
|
||||
// Smooth curve, not jumpy angles
|
||||
<motion.div
|
||||
animate={{ x: [0, 50, 100], y: [0, -25, 0] }}
|
||||
transition={{ duration: 1, ease: "easeInOut" }}
|
||||
/>
|
||||
```
|
||||
|
||||
### Figure-Ground
|
||||
Important elements animate while backgrounds stay stable:
|
||||
```tsx
|
||||
// Background fades out, modal animates in
|
||||
<>
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 0.5 }}
|
||||
className="fixed inset-0 bg-black"
|
||||
/>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.95 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
className="fixed inset-0 flex items-center justify-center"
|
||||
>
|
||||
Modal Content
|
||||
</motion.div>
|
||||
</>
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- [Framer Motion Documentation](https://www.framer.com/motion/)
|
||||
- [CSS Easing Functions](https://easings.net/)
|
||||
- [Material Design Motion](https://m2.material.io/design/motion/)
|
||||
- [Web Animations API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API)
|
||||
604
skills/controlled-ux-designer/RESPONSIVE-DESIGN.md
Normal file
604
skills/controlled-ux-designer/RESPONSIVE-DESIGN.md
Normal file
@@ -0,0 +1,604 @@
|
||||
# Responsive Design Reference
|
||||
|
||||
Detailed reference for implementing responsive, mobile-first designs.
|
||||
|
||||
## Mobile-First Approach
|
||||
|
||||
Always start with mobile design, then progressively enhance for larger screens.
|
||||
|
||||
**Why Mobile-First:**
|
||||
- Forces focus on essential content
|
||||
- Easier to scale up than scale down
|
||||
- Better performance on mobile devices
|
||||
- Aligns with usage patterns (mobile-first web)
|
||||
|
||||
## Breakpoint Strategy
|
||||
|
||||
### Standard Breakpoints
|
||||
|
||||
```css
|
||||
/* Mobile First Approach */
|
||||
/* Base styles: 0-640px (mobile) */
|
||||
|
||||
/* Small tablets and large phones */
|
||||
@media (min-width: 640px) { }
|
||||
|
||||
/* Tablets */
|
||||
@media (min-width: 768px) { }
|
||||
|
||||
/* Small laptops */
|
||||
@media (min-width: 1024px) { }
|
||||
|
||||
/* Desktops */
|
||||
@media (min-width: 1280px) { }
|
||||
|
||||
/* Large desktops */
|
||||
@media (min-width: 1536px) { }
|
||||
```
|
||||
|
||||
### Specific Breakpoint Ranges
|
||||
|
||||
| Range | Pixels | Target Devices | Layout Strategy |
|
||||
|-------|--------|----------------|-----------------|
|
||||
| **XS** | 0-479px | Small phones (iPhone SE, older Android) | Single column, stacked navigation, large touch targets (min 44px) |
|
||||
| **SM** | 480-767px | Large phones (iPhone 14, most modern phones) | Single column, simplified UI, bottom navigation, reduced complexity |
|
||||
| **MD** | 768-1023px | Tablets (iPad, Android tablets) | 2 columns possible, sidebar navigation, some desktop features |
|
||||
| **LG** | 1024-1439px | Small laptops, landscape tablets | Multi-column layouts, full navigation, desktop UI patterns |
|
||||
| **XL** | 1440px+ | Desktop monitors, large screens | Max-width containers, multi-panel layouts, advanced features visible |
|
||||
|
||||
**Mobile Simplification Examples:**
|
||||
|
||||
- **Navigation**: Hamburger menu (mobile) → Full nav bar (desktop)
|
||||
- **Forms**: Stacked fields (mobile) → Side-by-side fields (desktop)
|
||||
- **Content**: Single column (mobile) → Multi-column grid (desktop)
|
||||
- **Actions**: Fixed bottom bar (mobile) → Inline buttons (desktop)
|
||||
- **Tables**: Collapsed cards (mobile) → Full data table (desktop)
|
||||
- **Sidebars**: Hidden/collapsible (mobile) → Always visible (desktop)
|
||||
- **Filters**: Modal/drawer (mobile) → Sidebar panel (desktop)
|
||||
|
||||
### Tailwind Responsive Classes
|
||||
|
||||
```tsx
|
||||
<div className="
|
||||
w-full // mobile: full width
|
||||
sm:w-1/2 // small: half width
|
||||
md:w-1/3 // medium: third width
|
||||
lg:w-1/4 // large: quarter width
|
||||
">
|
||||
Responsive width
|
||||
</div>
|
||||
```
|
||||
|
||||
## Responsive Images
|
||||
|
||||
### Using srcset for Responsive Images
|
||||
|
||||
```tsx
|
||||
<img
|
||||
src="image-800w.jpg"
|
||||
srcSet="
|
||||
image-400w.jpg 400w,
|
||||
image-800w.jpg 800w,
|
||||
image-1200w.jpg 1200w
|
||||
"
|
||||
sizes="
|
||||
(max-width: 640px) 100vw,
|
||||
(max-width: 1024px) 50vw,
|
||||
33vw
|
||||
"
|
||||
alt="Descriptive alt text"
|
||||
loading="lazy"
|
||||
/>
|
||||
```
|
||||
|
||||
### Next.js Image Component
|
||||
|
||||
```tsx
|
||||
import Image from 'next/image';
|
||||
|
||||
<Image
|
||||
src="/hero-image.jpg"
|
||||
alt="Descriptive alt text"
|
||||
width={1200}
|
||||
height={600}
|
||||
priority // for above-the-fold images
|
||||
className="w-full h-auto"
|
||||
/>
|
||||
```
|
||||
|
||||
## Responsive Typography
|
||||
|
||||
### Fluid Typography with Tailwind
|
||||
|
||||
```tsx
|
||||
<h1 className="
|
||||
text-3xl // mobile: 30px
|
||||
sm:text-4xl // small: 36px
|
||||
md:text-5xl // medium: 48px
|
||||
lg:text-6xl // large: 60px
|
||||
">
|
||||
Responsive Headline
|
||||
</h1>
|
||||
```
|
||||
|
||||
### Fluid Typography with CSS Clamp
|
||||
|
||||
```css
|
||||
h1 {
|
||||
/* min: 2rem (32px), preferred: 5vw, max: 4rem (64px) */
|
||||
font-size: clamp(2rem, 5vw, 4rem);
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
p {
|
||||
/* min: 1rem (16px), preferred: 2.5vw, max: 1.25rem (20px) */
|
||||
font-size: clamp(1rem, 2.5vw, 1.25rem);
|
||||
line-height: 1.6;
|
||||
}
|
||||
```
|
||||
|
||||
## Responsive Layouts
|
||||
|
||||
### CSS Grid Responsive Pattern
|
||||
|
||||
```tsx
|
||||
<div className="
|
||||
grid
|
||||
grid-cols-1 // mobile: 1 column
|
||||
sm:grid-cols-2 // small: 2 columns
|
||||
md:grid-cols-3 // medium: 3 columns
|
||||
lg:grid-cols-4 // large: 4 columns
|
||||
gap-4 // consistent gap
|
||||
">
|
||||
{items.map(item => (
|
||||
<Card key={item.id}>{item.content}</Card>
|
||||
))}
|
||||
</div>
|
||||
```
|
||||
|
||||
### Flexbox Responsive Pattern
|
||||
|
||||
```tsx
|
||||
<div className="
|
||||
flex
|
||||
flex-col // mobile: stack vertically
|
||||
md:flex-row // medium+: horizontal layout
|
||||
gap-6
|
||||
items-center
|
||||
">
|
||||
<div className="w-full md:w-1/2">Content Left</div>
|
||||
<div className="w-full md:w-1/2">Content Right</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Touch-Friendly Interfaces
|
||||
|
||||
### Touch Target Sizing
|
||||
|
||||
```tsx
|
||||
// Minimum 44x44px touch targets
|
||||
<button className="
|
||||
min-w-[44px]
|
||||
min-h-[44px]
|
||||
px-4 py-2
|
||||
rounded-lg
|
||||
touch-manipulation // prevents 300ms tap delay
|
||||
">
|
||||
Tap Me
|
||||
</button>
|
||||
```
|
||||
|
||||
### Touch Gestures
|
||||
|
||||
```tsx
|
||||
// Consider common mobile gestures
|
||||
<div className="
|
||||
overflow-x-auto // horizontal scroll
|
||||
snap-x // snap scrolling
|
||||
snap-mandatory
|
||||
overscroll-contain // prevent bounce on mobile
|
||||
">
|
||||
{/* Scrollable content */}
|
||||
</div>
|
||||
```
|
||||
|
||||
## Navigation Patterns
|
||||
|
||||
### Mobile Menu Pattern
|
||||
|
||||
```tsx
|
||||
import { useState } from 'react';
|
||||
import { List, X } from '@phosphor-icons/react';
|
||||
|
||||
export function MobileNav() {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Mobile menu button */}
|
||||
<button
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
className="md:hidden p-2"
|
||||
aria-label="Toggle menu"
|
||||
>
|
||||
{isOpen ? <X size={24} /> : <List size={24} />}
|
||||
</button>
|
||||
|
||||
{/* Mobile menu overlay */}
|
||||
{isOpen && (
|
||||
<div className="
|
||||
fixed inset-0 z-50 bg-white
|
||||
md:hidden
|
||||
">
|
||||
<nav className="p-6 space-y-4">
|
||||
{/* Navigation items */}
|
||||
</nav>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Desktop navigation */}
|
||||
<nav className="hidden md:flex gap-6">
|
||||
{/* Navigation items */}
|
||||
</nav>
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Sticky Navigation
|
||||
|
||||
```tsx
|
||||
<header className="
|
||||
sticky top-0 z-40
|
||||
bg-white/80
|
||||
backdrop-blur-md
|
||||
border-b border-slate-200
|
||||
">
|
||||
<nav className="container mx-auto px-4 py-4">
|
||||
{/* Navigation content */}
|
||||
</nav>
|
||||
</header>
|
||||
```
|
||||
|
||||
## Responsive Forms
|
||||
|
||||
### Form Layout Pattern
|
||||
|
||||
```tsx
|
||||
<form className="space-y-6">
|
||||
{/* Full width on mobile, side-by-side on desktop */}
|
||||
<div className="
|
||||
grid
|
||||
grid-cols-1
|
||||
md:grid-cols-2
|
||||
gap-4
|
||||
">
|
||||
<div>
|
||||
<label htmlFor="firstName" className="block text-sm font-medium mb-1">
|
||||
First Name
|
||||
</label>
|
||||
<input
|
||||
id="firstName"
|
||||
type="text"
|
||||
className="
|
||||
w-full px-4 py-2
|
||||
border border-slate-300
|
||||
rounded-lg
|
||||
focus:ring-2 focus:ring-blue-500
|
||||
touch-manipulation
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="lastName" className="block text-sm font-medium mb-1">
|
||||
Last Name
|
||||
</label>
|
||||
<input
|
||||
id="lastName"
|
||||
type="text"
|
||||
className="
|
||||
w-full px-4 py-2
|
||||
border border-slate-300
|
||||
rounded-lg
|
||||
focus:ring-2 focus:ring-blue-500
|
||||
touch-manipulation
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Full width textarea */}
|
||||
<div>
|
||||
<label htmlFor="message" className="block text-sm font-medium mb-1">
|
||||
Message
|
||||
</label>
|
||||
<textarea
|
||||
id="message"
|
||||
rows={4}
|
||||
className="
|
||||
w-full px-4 py-2
|
||||
border border-slate-300
|
||||
rounded-lg
|
||||
focus:ring-2 focus:ring-blue-500
|
||||
touch-manipulation
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
className="
|
||||
w-full md:w-auto
|
||||
px-8 py-3
|
||||
bg-blue-600 text-white
|
||||
rounded-lg
|
||||
hover:bg-blue-700
|
||||
transition-colors
|
||||
touch-manipulation
|
||||
"
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
</form>
|
||||
```
|
||||
|
||||
## Responsive Content Hiding
|
||||
|
||||
### Show/Hide Based on Screen Size
|
||||
|
||||
```tsx
|
||||
<div>
|
||||
{/* Show only on mobile */}
|
||||
<div className="block md:hidden">
|
||||
Mobile content
|
||||
</div>
|
||||
|
||||
{/* Show only on tablet and up */}
|
||||
<div className="hidden md:block">
|
||||
Desktop content
|
||||
</div>
|
||||
|
||||
{/* Show only on desktop */}
|
||||
<div className="hidden lg:block">
|
||||
Large screen content
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### Lazy Loading Images
|
||||
|
||||
```tsx
|
||||
<img
|
||||
src="image.jpg"
|
||||
alt="Description"
|
||||
loading="lazy"
|
||||
className="w-full h-auto"
|
||||
/>
|
||||
```
|
||||
|
||||
### Responsive Video
|
||||
|
||||
```tsx
|
||||
<div className="relative aspect-video">
|
||||
<video
|
||||
className="absolute inset-0 w-full h-full object-cover"
|
||||
poster="thumbnail.jpg"
|
||||
controls
|
||||
preload="metadata"
|
||||
>
|
||||
<source src="video-mobile.mp4" media="(max-width: 640px)" />
|
||||
<source src="video-desktop.mp4" />
|
||||
</video>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Testing Responsive Designs
|
||||
|
||||
### Browser DevTools
|
||||
|
||||
1. Open Chrome/Firefox DevTools (F12)
|
||||
2. Toggle device toolbar (Ctrl+Shift+M)
|
||||
3. Test common breakpoints:
|
||||
- iPhone SE (375px)
|
||||
- iPhone 12 Pro (390px)
|
||||
- iPad (768px)
|
||||
- iPad Pro (1024px)
|
||||
- Desktop (1280px+)
|
||||
|
||||
### Real Device Testing
|
||||
|
||||
**Essential devices to test:**
|
||||
- Small phone (iPhone SE, Android small)
|
||||
- Large phone (iPhone Pro Max, Android large)
|
||||
- Tablet (iPad, Android tablet)
|
||||
- Desktop (various resolutions)
|
||||
|
||||
### Playwright Testing
|
||||
|
||||
```typescript
|
||||
// Use playwright MCP to test responsive breakpoints
|
||||
await page.setViewportSize({ width: 375, height: 667 }); // iPhone SE
|
||||
await page.screenshot({ path: 'mobile.png' });
|
||||
|
||||
await page.setViewportSize({ width: 768, height: 1024 }); // iPad
|
||||
await page.screenshot({ path: 'tablet.png' });
|
||||
|
||||
await page.setViewportSize({ width: 1920, height: 1080 }); // Desktop
|
||||
await page.screenshot({ path: 'desktop.png' });
|
||||
```
|
||||
|
||||
## Common Responsive Patterns
|
||||
|
||||
### Card Grid
|
||||
|
||||
```tsx
|
||||
<div className="
|
||||
grid
|
||||
grid-cols-1
|
||||
sm:grid-cols-2
|
||||
lg:grid-cols-3
|
||||
xl:grid-cols-4
|
||||
gap-6
|
||||
p-4
|
||||
">
|
||||
{items.map(item => (
|
||||
<article
|
||||
key={item.id}
|
||||
className="
|
||||
bg-white
|
||||
rounded-lg
|
||||
border border-slate-200
|
||||
overflow-hidden
|
||||
hover:shadow-lg
|
||||
transition-shadow
|
||||
"
|
||||
>
|
||||
<img
|
||||
src={item.image}
|
||||
alt={item.title}
|
||||
className="w-full h-48 object-cover"
|
||||
loading="lazy"
|
||||
/>
|
||||
<div className="p-4">
|
||||
<h3 className="text-lg font-semibold mb-2">{item.title}</h3>
|
||||
<p className="text-slate-600">{item.description}</p>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
```
|
||||
|
||||
### Hero Section
|
||||
|
||||
```tsx
|
||||
<section className="
|
||||
relative
|
||||
min-h-screen
|
||||
flex items-center
|
||||
px-4 sm:px-6 lg:px-8
|
||||
">
|
||||
<div className="
|
||||
max-w-7xl mx-auto w-full
|
||||
grid grid-cols-1 lg:grid-cols-2
|
||||
gap-12
|
||||
items-center
|
||||
">
|
||||
<div className="space-y-6">
|
||||
<h1 className="
|
||||
text-4xl sm:text-5xl lg:text-6xl
|
||||
font-bold
|
||||
tracking-tight
|
||||
">
|
||||
Your Headline Here
|
||||
</h1>
|
||||
<p className="
|
||||
text-lg sm:text-xl
|
||||
text-slate-600
|
||||
max-w-2xl
|
||||
">
|
||||
Supporting description that works across all screen sizes.
|
||||
</p>
|
||||
<div className="
|
||||
flex flex-col sm:flex-row
|
||||
gap-4
|
||||
">
|
||||
<button className="
|
||||
px-8 py-3
|
||||
bg-blue-600 text-white
|
||||
rounded-lg
|
||||
hover:bg-blue-700
|
||||
transition-colors
|
||||
">
|
||||
Primary Action
|
||||
</button>
|
||||
<button className="
|
||||
px-8 py-3
|
||||
border-2 border-slate-300
|
||||
rounded-lg
|
||||
hover:border-slate-400
|
||||
transition-colors
|
||||
">
|
||||
Secondary Action
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="
|
||||
relative
|
||||
aspect-square
|
||||
rounded-2xl
|
||||
overflow-hidden
|
||||
">
|
||||
<img
|
||||
src="hero-image.jpg"
|
||||
alt="Hero"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
```
|
||||
|
||||
## Accessibility Considerations
|
||||
|
||||
### Focus Management on Mobile
|
||||
|
||||
```tsx
|
||||
<button
|
||||
className="
|
||||
focus:outline-none
|
||||
focus:ring-4 focus:ring-blue-500
|
||||
focus:ring-offset-2
|
||||
rounded-lg
|
||||
"
|
||||
aria-label="Descriptive label"
|
||||
>
|
||||
Action
|
||||
</button>
|
||||
```
|
||||
|
||||
### Skip Links
|
||||
|
||||
```tsx
|
||||
<a
|
||||
href="#main-content"
|
||||
className="
|
||||
sr-only
|
||||
focus:not-sr-only
|
||||
focus:absolute
|
||||
focus:top-4 focus:left-4
|
||||
focus:z-50
|
||||
focus:px-4 focus:py-2
|
||||
focus:bg-blue-600 focus:text-white
|
||||
focus:rounded-lg
|
||||
"
|
||||
>
|
||||
Skip to main content
|
||||
</a>
|
||||
```
|
||||
|
||||
## Best Practices Summary
|
||||
|
||||
✅ **Do:**
|
||||
- Start with mobile design first
|
||||
- Use relative units (rem, em, %) for flexibility
|
||||
- Test on real devices, not just emulators
|
||||
- Ensure touch targets are at least 44x44px
|
||||
- Use semantic HTML for better accessibility
|
||||
- Implement lazy loading for images and videos
|
||||
- Optimize assets for mobile networks
|
||||
- Use CSS Grid and Flexbox for flexible layouts
|
||||
- Provide adequate spacing between interactive elements
|
||||
|
||||
❌ **Don't:**
|
||||
- Design for desktop first and scale down
|
||||
- Use fixed pixel widths for layout containers
|
||||
- Rely solely on browser DevTools for testing
|
||||
- Make touch targets too small
|
||||
- Forget keyboard navigation
|
||||
- Load all images eagerly
|
||||
- Use large unoptimized images on mobile
|
||||
- Use complex nested tables for layout
|
||||
- Place important actions in hard-to-reach areas
|
||||
738
skills/controlled-ux-designer/SKILL.md
Normal file
738
skills/controlled-ux-designer/SKILL.md
Normal file
@@ -0,0 +1,738 @@
|
||||
---
|
||||
name: controlled-ux-designer
|
||||
description: Expert UI/UX design guidance for unique, accessible interfaces. Use for visual decisions, colors, typography, layouts. Always ask before making design decisions. Use this skill when the user asks to build web components, pages, or applications.
|
||||
metadata:
|
||||
version: 1.0.0
|
||||
---
|
||||
|
||||
# UX Designer
|
||||
|
||||
Expert UI/UX design skill that helps create unique, accessible, and thoughtfully designed interfaces. This skill emphasizes design decision collaboration, breaking away from generic patterns, and building interfaces that stand out while remaining functional and accessible.
|
||||
|
||||
## Core Philosophy
|
||||
|
||||
**CRITICAL: Design Decision Protocol**
|
||||
- **ALWAYS ASK** before making any design decisions (colors, fonts, sizes, layouts)
|
||||
- Never implement design changes until explicitly instructed
|
||||
- The guidelines below are practical guidance for when design decisions are approved
|
||||
- Present alternatives and trade-offs, not single "correct" solutions
|
||||
|
||||
## Foundational Design Principles
|
||||
|
||||
### Stand Out From Generic Patterns
|
||||
|
||||
**Avoid Generic Training Dataset Patterns:**
|
||||
- Don't default to "Claude style" designs (excessive bauhaus, liquid glass, apple-like)
|
||||
- Don't use generic SaaS aesthetics that look machine-generated
|
||||
- Don't rely only on solid colors - suggest photography, patterns, textures
|
||||
- Think beyond typical patterns - you can step off the written path
|
||||
|
||||
**Draw Inspiration From:**
|
||||
- Modern landing pages (Perplexity, Comet Browser, Dia Browser)
|
||||
- Framer templates and their innovative approaches
|
||||
- Leading brand design studios
|
||||
- Historical design movements (Bauhaus, Otl Aicher, Braun) - but as inspiration, not imitation
|
||||
- Beautiful background animations (CSS, SVG) - slow, looping, subtle
|
||||
|
||||
**Visual Interest Strategies:**
|
||||
- Unique color pairs that aren't typical
|
||||
- Animation effects that feel fresh
|
||||
- Background patterns that add depth without distraction
|
||||
- Typography combinations that create contrast
|
||||
- Visual assets that tell a story
|
||||
|
||||
### Core Design Philosophy
|
||||
|
||||
1. **Simplicity Through Reduction**
|
||||
- Identify the essential purpose and eliminate distractions
|
||||
- Begin with complexity, then deliberately remove until reaching the simplest effective solution
|
||||
- Every element must justify its existence
|
||||
|
||||
2. **Material Honesty**
|
||||
- Digital materials have unique properties - embrace them
|
||||
- Buttons should communicate affordance through color, spacing, and typography (not shadows)
|
||||
- Cards use borders and background differentiation (not depth effects)
|
||||
- Animations follow real-world physics principles adapted to digital responsiveness
|
||||
|
||||
**Examples:**
|
||||
- Clickable: Use distinct colors, hover state changes, cursor feedback
|
||||
- Containers: Use subtle borders (1px), background color shifts, or generous padding
|
||||
- Hierarchy: Use scale, weight, and spacing rather than elevation
|
||||
|
||||
3. **Functional Layering (Not Visual Depth)**
|
||||
- Create hierarchy through typography scale, color contrast, and spatial relationships
|
||||
- Layer information conceptually (primary → secondary → tertiary)
|
||||
- Reject skeuomorphic shadows/gradients that imitate physical depth
|
||||
- Embrace functional depth: modals over content, dropdowns over UI
|
||||
|
||||
4. **Obsessive Detail**
|
||||
- Consider every pixel, interaction, and transition
|
||||
- Excellence emerges from hundreds of small, intentional decisions
|
||||
- Balance: Details should serve simplicity, not complexity
|
||||
- When detail conflicts with clarity, clarity wins
|
||||
|
||||
5. **Coherent Design Language**
|
||||
- Every element should visually communicate its function
|
||||
- Elements should feel part of a unified system
|
||||
- Nothing should feel arbitrary
|
||||
|
||||
6. **Invisibility of Technology**
|
||||
- The best technology disappears
|
||||
- Users should focus on content and goals, not on understanding the interface
|
||||
|
||||
### What This Means in Practice
|
||||
|
||||
**Color Usage:**
|
||||
- Base palette: 4-5 neutral shades (backgrounds, borders, text)
|
||||
- Accent palette: 1-3 bold colors (CTAs, status, emphasis)
|
||||
- Neutrals are slightly desaturated, warm or cool based on brand intent
|
||||
- Accents are saturated enough to create clear contrast
|
||||
|
||||
**Typography:**
|
||||
- Headlines: Emotional, attention-grabbing (personality over pure legibility)
|
||||
- Body/UI: Functional, highly legible (clarity over expression)
|
||||
- 2-3 typefaces maximum
|
||||
- Clear mathematical scale (e.g., 1.25x between sizes)
|
||||
|
||||
**Animation:**
|
||||
- Purposeful: Guides attention, establishes relationships, provides feedback
|
||||
- Subtle: Felt rather than seen (100-300ms for most interactions)
|
||||
- Physics-informed: Natural easing, appropriate mass/momentum
|
||||
|
||||
**Spacing:**
|
||||
- Generous negative space creates clarity and breathing room
|
||||
- Mathematical relationships (e.g., 4px base, 8/16/24/32/48px scale)
|
||||
- Consistent application creates visual rhythm
|
||||
|
||||
### Design Decision Checklist
|
||||
|
||||
Before presenting any design, verify:
|
||||
|
||||
1. **Purpose**: Does every element serve a clear function?
|
||||
2. **Hierarchy**: Is visual importance aligned with content importance?
|
||||
3. **Consistency**: Do similar elements look and behave similarly?
|
||||
4. **Accessibility**: Does it meet WCAG AA standards? (contrast, touch targets, keyboard nav)
|
||||
5. **Responsiveness**: Does it work on mobile, tablet, desktop?
|
||||
6. **Uniqueness**: Does this break from generic SaaS patterns?
|
||||
7. **Approval**: Have I asked before implementing colors, fonts, sizes, layouts?
|
||||
|
||||
**Design System Framework:**
|
||||
|
||||
For understanding what's fixed (universal rules), project-specific (brand personality), and adaptable (context-dependent) in your design system, see DESIGN-SYSTEM-TEMPLATE.md (meta-framework, project templates, decision trees).
|
||||
|
||||
## Visual Design Standards
|
||||
|
||||
### Color & Contrast
|
||||
|
||||
**Color System Architecture:**
|
||||
|
||||
Every interface needs two color roles:
|
||||
|
||||
1. **Base/Neutral Palette (4-5 colors):**
|
||||
- Backgrounds (lightest)
|
||||
- Surface colors (cards, inputs)
|
||||
- Borders and dividers
|
||||
- Text (darkest)
|
||||
- Use slightly desaturated, warm or cool greys based on brand
|
||||
|
||||
2. **Accent Palette (1-3 colors):**
|
||||
- Primary action (CTA buttons)
|
||||
- Status indicators (success, warning, error, info)
|
||||
- Focus/hover states
|
||||
- Use saturated colors for clear contrast against neutrals
|
||||
|
||||
**Palette Structure Example:**
|
||||
```
|
||||
Neutrals: slate-50, slate-100, slate-300, slate-700, slate-900
|
||||
Accents: teal-500 (primary), amber-500 (warning), red-500 (error)
|
||||
```
|
||||
|
||||
**Color Application Rules:**
|
||||
|
||||
- **Backgrounds**: Lightest neutral (slate-50 or white)
|
||||
- **Text**: Darkest neutral for primary text (slate-900), mid-tone for secondary (slate-600)
|
||||
- **Buttons (primary)**: Accent color with white text
|
||||
- **Buttons (secondary)**: Neutral with border and dark text
|
||||
- **Status indicators**: Specific accent (green=success, red=error, amber=warning, blue=info)
|
||||
- **Interactive states**:
|
||||
- Hover: Darken by 10-15% or shift hue slightly
|
||||
- Focus: Use ring/outline in accent color
|
||||
- Disabled: Reduce opacity to 40-50% and remove hover effects
|
||||
|
||||
**Color Relationships:**
|
||||
|
||||
Choose warm or cool intentionally based on brand:
|
||||
- **Warm greys** (beige/brown undertones): Organic, approachable, trustworthy
|
||||
- **Cool greys** (blue undertones): Modern, tech-forward, professional
|
||||
|
||||
Accent colors should have clear contrast with both:
|
||||
- Light backgrounds (for buttons on white)
|
||||
- Dark text (if used as backgrounds for white text)
|
||||
|
||||
**Intentional Color Usage:**
|
||||
- Every color must serve a purpose (hierarchy, function, status, or action)
|
||||
- Avoid decorative colors that don't communicate meaning
|
||||
- Maintain consistency: same color = same meaning throughout
|
||||
|
||||
**Accessibility:**
|
||||
- Ensure sufficient contrast for color-blind users
|
||||
- Follow WCAG 2.1 AA: minimum 4.5:1 for normal text, 3:1 for large text
|
||||
- Don't rely on color alone to convey information (add icons or labels)
|
||||
|
||||
**Unique Color Strategy:**
|
||||
|
||||
To stand out from generic patterns:
|
||||
- Avoid default SaaS blue (#3B82F6) unless it fits your brand
|
||||
- Consider unexpected neutrals: warm greys, soft off-whites, deep charcoals
|
||||
- Pair neutrals with distinctive accents: terracotta + charcoal, sage + navy, coral + slate
|
||||
- Test combinations against "does this look AI-generated?" filter
|
||||
|
||||
### Typography Excellence
|
||||
|
||||
**Typography Philosophy:**
|
||||
|
||||
Typography is a primary design element that conveys personality and hierarchy.
|
||||
|
||||
**Functional vs Emotional Typography:**
|
||||
- **Headlines/Display**: Prioritize emotion, personality, attention (legibility secondary)
|
||||
- **Body Text**: Prioritize legibility, reading comfort, accessibility
|
||||
- **UI/Labels**: Prioritize clarity, scannability, consistency
|
||||
|
||||
**Font Selection:**
|
||||
- Use 2-3 typefaces maximum
|
||||
- Limit to 3 weights per typeface (e.g., Regular 400, Medium 500, Bold 700)
|
||||
- Prefer variable fonts for fine-tuned control and performance
|
||||
|
||||
**Font Version Usage:**
|
||||
- **Display version**: Headlines and hero text only
|
||||
- **Text version**: Paragraphs and long-form content
|
||||
- **Caption/Micro**: Small UI labels (1-2 lines, non-critical info)
|
||||
|
||||
**Recommended Sources:**
|
||||
- Google Fonts for web (free, well-optimized, reliable)
|
||||
- System fonts for performance-critical apps (-apple-system, BlinkMacSystemFont, Segoe UI)
|
||||
- Choose fonts that serve your brand's purpose (not "trending" lists)
|
||||
|
||||
**Typographic Scale:**
|
||||
|
||||
Use mathematical relationships for size hierarchy:
|
||||
- **Ratio**: Major third (1.25x) for moderate contrast, Perfect fourth (1.333x) for dramatic
|
||||
- **Base size**: 16px (1rem) for body text
|
||||
- **Example scale (1.25x)**:
|
||||
```
|
||||
xs: 0.64rem (10px)
|
||||
sm: 0.8rem (13px)
|
||||
base: 1rem (16px)
|
||||
lg: 1.25rem (20px)
|
||||
xl: 1.563rem (25px)
|
||||
2xl: 1.953rem (31px)
|
||||
3xl: 2.441rem (39px)
|
||||
4xl: 3.052rem (49px)
|
||||
5xl: 3.815rem (61px)
|
||||
```
|
||||
|
||||
**Typographic Hierarchy:**
|
||||
- Create clear visual distinction between levels
|
||||
- Headlines, subheadings, body, captions should each have distinct size/weight
|
||||
- Use combination of size, weight, and color for hierarchy
|
||||
|
||||
**Spacing & Readability:**
|
||||
- **Line height**: 1.5x font size for body text (e.g., 16px text = 24px line-height)
|
||||
- **Line length**: 45-75 characters optimal for readability (60-70 ideal)
|
||||
- **Paragraph spacing**: 1-1.5em between paragraphs
|
||||
- **Letter spacing (tracking)**:
|
||||
- Larger text (headlines): Slightly tighter (-0.02em to -0.05em)
|
||||
- Normal text (body): Default (0)
|
||||
- Small text (captions): Slightly looser (+0.01em to +0.03em)
|
||||
- General rule: As size increases, reduce tracking; as size decreases, increase tracking
|
||||
|
||||
**Font Pairing Logic:**
|
||||
|
||||
When using multiple typefaces, create contrast through:
|
||||
- **Category contrast**: Serif + Sans-serif (classic, clear distinction)
|
||||
- **Weight contrast**: Light + Bold (dynamic, energetic)
|
||||
- **Personality contrast**: Geometric + Humanist (modern + warm)
|
||||
|
||||
Examples:
|
||||
- Serif headlines + Sans body (editorial, trustworthy)
|
||||
- Display headlines + System body (distinctive + efficient)
|
||||
- Bold sans headlines + Light sans body (modern, clean)
|
||||
|
||||
**UI Typography:**
|
||||
|
||||
Specific guidance for interface elements:
|
||||
- **Button text**: Semi-Bold (600), 14-16px, consistent casing (all-caps OR title case)
|
||||
- **Form labels**: Regular (400), 14px, positioned above input
|
||||
- **Form input text**: Regular (400), 16px minimum (prevents iOS zoom on focus)
|
||||
- **Placeholder text**: Light (300) or desaturated color, same size as input
|
||||
- **Error messages**: Regular (400), 12-14px, color-coded (red-ish)
|
||||
|
||||
**Responsive Typography:**
|
||||
|
||||
Scale type sizes across breakpoints:
|
||||
```tsx
|
||||
// Example with Tailwind
|
||||
<h1 className="text-3xl md:text-4xl lg:text-5xl">
|
||||
Responsive Headline
|
||||
</h1>
|
||||
|
||||
// Or with CSS clamp (fluid)
|
||||
h1 {
|
||||
font-size: clamp(2rem, 5vw, 4rem);
|
||||
}
|
||||
```
|
||||
|
||||
Reduce sizes on mobile (20-30% smaller than desktop)
|
||||
Reduce hierarchy levels on small screens (fewer distinct sizes)
|
||||
|
||||
### Layout & Spatial Design
|
||||
|
||||
**Compositional Balance:**
|
||||
- Every screen should feel balanced
|
||||
- Pay attention to visual weight and negative space
|
||||
- Use generous negative space to focus attention
|
||||
- Add sufficient margins and paddings for professional, spacious look
|
||||
|
||||
**Grid Discipline:**
|
||||
- Maintain consistent underlying grid system
|
||||
- Create sense of order while allowing meaningful exceptions
|
||||
- Use grid/flex wrappers with `gap` for spacing
|
||||
- Prioritize wrappers over direct margins/padding on children
|
||||
|
||||
**Spatial Relationships:**
|
||||
- Group related elements through proximity, alignment, and shared attributes
|
||||
- Use size, color, and spacing to highlight important elements
|
||||
- Guide user focus through visual hierarchy
|
||||
|
||||
**Attention Guidance:**
|
||||
- Design interfaces that guide user attention effectively
|
||||
- Avoid cluttered interfaces where elements compete
|
||||
- Create clear paths through the content
|
||||
|
||||
## Interaction Design
|
||||
|
||||
### Motion & Animation
|
||||
|
||||
**Purposeful Animation:**
|
||||
|
||||
Every animation must serve a functional purpose:
|
||||
- **Orient users**: Smooth transitions during navigation changes
|
||||
- **Establish relationships**: Show how elements connect (expand from source, slide between states)
|
||||
- **Provide feedback**: Confirm interactions (button press, form submission)
|
||||
- **Guide attention**: Direct focus to important changes (new messages, errors)
|
||||
|
||||
**Animation & Gestalt Principles:**
|
||||
|
||||
Motion should reinforce visual relationships:
|
||||
- **Proximity**: Elements near each other move together (grouped cards animating)
|
||||
- **Similarity**: Similar elements animate similarly (all buttons have same hover timing)
|
||||
- **Continuity**: Movement follows natural paths (smooth curves, not jumpy angles)
|
||||
- **Figure-ground**: Important elements animate while backgrounds stay stable
|
||||
|
||||
**Natural Physics:**
|
||||
|
||||
Animations should feel organic, not mechanical:
|
||||
- **Easing**: Use ease-out for entrances (fast start, slow end)
|
||||
- **Easing**: Use ease-in for exits (slow start, fast end)
|
||||
- **Easing**: Use ease-in-out for transitions (smooth both ends)
|
||||
- Avoid linear easing (feels robotic) except for continuous loops
|
||||
- Apply appropriate mass/momentum (lightweight UI vs weighty modals)
|
||||
|
||||
**Subtle Restraint:**
|
||||
- Animations should be felt rather than seen
|
||||
- Don't delay user actions unnecessarily (keep under 300ms for interactive feedback)
|
||||
- Never block critical actions with decorative animations
|
||||
- Respect `prefers-reduced-motion` media query
|
||||
|
||||
**Timing Guidelines:**
|
||||
|
||||
- **Micro-interactions** (button press, checkbox toggle): 100-150ms
|
||||
- **State changes** (expanding accordion, tab switch): 200-300ms
|
||||
- **Page transitions** (route changes, modal open/close): 300-500ms
|
||||
- **Attention-directing** (notification appearance, error highlight): 200-400ms
|
||||
|
||||
**Physics Profiles:**
|
||||
|
||||
Define consistent durations for element types:
|
||||
- **Lightweight** (icons, small UI): 150ms
|
||||
- **Standard** (cards, panels): 300ms
|
||||
- **Weighty** (modals, page transitions): 500ms
|
||||
|
||||
**Performance Optimization:**
|
||||
|
||||
- Animate `transform` and `opacity` only (GPU-accelerated, smooth 60fps)
|
||||
- Avoid animating `width`, `height`, `top`, `left`, `margin` (causes reflow/repaint)
|
||||
- Use `will-change` sparingly for complex animations (pre-allocates GPU resources)
|
||||
- Test on low-end devices (60fps on powerful hardware ≠ 60fps on mobile)
|
||||
|
||||
**Implementation:**
|
||||
- Use `framer-motion` sparingly and purposefully
|
||||
- Prefer CSS animations over JavaScript when possible (better performance)
|
||||
- Use CSS transitions for simple hover/focus states
|
||||
- Implement `@media (prefers-reduced-motion: reduce)` to disable/reduce animations
|
||||
|
||||
**Example:**
|
||||
```tsx
|
||||
// Simple hover transition
|
||||
<button className="
|
||||
transition-colors duration-200 ease-out
|
||||
bg-blue-600 hover:bg-blue-700
|
||||
">
|
||||
Click me
|
||||
</button>
|
||||
|
||||
// Framer Motion for complex interaction
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -20 }}
|
||||
transition={{ duration: 0.3, ease: "easeOut" }}
|
||||
>
|
||||
Content
|
||||
</motion.div>
|
||||
```
|
||||
|
||||
**Motion Specification:**
|
||||
|
||||
For detailed motion specs, see MOTION-SPEC.md (easing curves, duration tables, state-specific animations, implementation patterns).
|
||||
|
||||
### User Experience Patterns
|
||||
|
||||
**Core UX Principles:**
|
||||
|
||||
1. **Direct Manipulation**
|
||||
- Users interact directly with content, not through abstract controls
|
||||
- Examples:
|
||||
- Drag & drop to reorder items (not up/down buttons)
|
||||
- Inline editing (click to edit, not separate form)
|
||||
- Sliders for ranges (not numeric input with +/-)
|
||||
- Pinch/zoom gestures on mobile (not +/- buttons)
|
||||
|
||||
2. **Immediate Feedback**
|
||||
- Every interaction provides instantaneous visual feedback (within 100ms)
|
||||
- Types of feedback:
|
||||
- **Visual**: Button pressed state, hover effects, color changes
|
||||
- **Haptic**: Vibration on mobile (submit, error, success)
|
||||
- **Audio**: Subtle sounds for critical actions (optional, user-controlled)
|
||||
- **Loading**: Skeleton screens, spinners for >300ms operations
|
||||
- **Success**: Checkmarks, green highlights, toast notifications
|
||||
- **Error**: Red highlights, inline error messages, shake animations
|
||||
|
||||
3. **Consistent Behavior**
|
||||
- Similar-looking elements behave similarly
|
||||
- Examples:
|
||||
- **Visual consistency**: All primary buttons have same colors, sizes, hover states
|
||||
- **Behavioral consistency**: All modals close via X button, ESC key, and outside click
|
||||
- **Interaction consistency**: All drag targets have same hover state and drop feedback
|
||||
- **Pattern consistency**: All forms validate on blur and submit
|
||||
|
||||
4. **Forgiveness**
|
||||
- Make errors difficult, but recovery easy
|
||||
- **Prevention strategies**:
|
||||
- Disable invalid actions (grey out unavailable buttons)
|
||||
- Validate inputs inline (before submission)
|
||||
- Confirm destructive actions (delete, overwrite)
|
||||
- Auto-save in background (drafts, progress)
|
||||
- **Recovery strategies**:
|
||||
- Undo/redo for all state changes
|
||||
- Soft deletes (trash/archive before permanent delete)
|
||||
- Clear error messages with actionable fixes
|
||||
- Preserve user input on errors (don't clear forms)
|
||||
|
||||
5. **Progressive Disclosure**
|
||||
- Reveal details as needed rather than overwhelming users
|
||||
- Levels of disclosure:
|
||||
- **Summary**: Show essential info by default (card title, price, rating)
|
||||
- **Details**: Expand to show more info (description, specs, reviews)
|
||||
- **Advanced**: Hide complex options behind "Advanced settings" toggle
|
||||
- Examples:
|
||||
- Accordion: Start collapsed, expand on click
|
||||
- Search filters: Show 3-5 common filters, hide rest behind "More filters"
|
||||
- Settings: Basic settings visible, advanced behind "Show advanced"
|
||||
|
||||
**Modern UX Patterns:**
|
||||
|
||||
1. **Conversational Interfaces**
|
||||
|
||||
Prioritize natural language interaction where appropriate:
|
||||
|
||||
**Four types:**
|
||||
- **Pure chat**: Full conversation (AI assistants, support bots)
|
||||
- **Command palette**: Text-based shortcuts (Cmd+K, search everywhere)
|
||||
- **Smart search**: Natural language queries (search "meetings next week" vs filtering)
|
||||
- **Form alternatives**: Conversational data collection ("What's your name?" vs form fields)
|
||||
|
||||
**When to use:**
|
||||
- Complex searches with multiple variables
|
||||
- Task guidance (wizards, onboarding)
|
||||
- Contextual help
|
||||
- Quick actions (command palette)
|
||||
|
||||
**When NOT to use:**
|
||||
- Simple forms (just use inputs)
|
||||
- Precise control interfaces (design tools, dashboards)
|
||||
- High-frequency repetitive tasks
|
||||
|
||||
2. **Adaptive Layouts**
|
||||
|
||||
Respond to user context automatically:
|
||||
- **Time-based**: Dark mode at night, light during day
|
||||
- **Device-based**: Simplified UI on mobile, full features on desktop
|
||||
- **Connection-based**: Reduce images/video on slow connections
|
||||
- **Usage-based**: Prioritize frequent actions, hide rarely-used features
|
||||
|
||||
Examples:
|
||||
- Auto dark/light mode based on time or system preference
|
||||
- Simplified mobile navigation (hamburger menu) vs full desktop nav
|
||||
- Collapsed sidebar on small screens, expanded on large
|
||||
|
||||
3. **Minimal, Flat Design**
|
||||
|
||||
Current aesthetic preference:
|
||||
- No drop shadows (except subtle ones for modals/dropdowns)
|
||||
- No gradients for depth (use for accents/backgrounds if desired)
|
||||
- No glass morphism effects
|
||||
- Focus on typography, color, and spacing to create hierarchy
|
||||
- Functional depth: Layers of content (modals, sheets) use positioning, not visual depth
|
||||
|
||||
**Navigation:**
|
||||
- Clear structure with intuitive navigation menus
|
||||
- Implement breadcrumbs for deep hierarchies (more than 2 levels)
|
||||
- Use standard UI patterns to reduce learning curve (hamburger menu, tab bars)
|
||||
- Ensure predictable behavior (back button works, links look clickable)
|
||||
- Maintain navigation context (highlight current page, preserve scroll position)
|
||||
|
||||
## Styling Implementation
|
||||
|
||||
### Component Library & Tools
|
||||
|
||||
**Component Library:**
|
||||
- Strongly prefer shadcn components (v4, pre-installed in `@/components/ui`)
|
||||
- Import individually: `import { Button } from "@/components/ui/button";`
|
||||
- Use over plain HTML elements (`<Button>` over `<button>`)
|
||||
- Avoid creating custom components with names that clash with shadcn
|
||||
|
||||
**Styling Engine:**
|
||||
- Use Tailwind utility classes exclusively
|
||||
- Adhere to theme variables in `index.css` via CSS custom properties
|
||||
- Map variables in `@theme` (see `tailwind.config.js`)
|
||||
- Use inline styles or CSS modules only when absolutely necessary
|
||||
|
||||
**Icons:**
|
||||
- Use `@phosphor-icons/react` for buttons and inputs
|
||||
- Example: `import { Plus } from "@phosphor-icons/react"; <Plus />`
|
||||
- Use color for plain icon buttons
|
||||
- Don't override default `size` or `weight` unless requested
|
||||
|
||||
**Notifications:**
|
||||
- Use `sonner` for toasts
|
||||
- Example: `import { toast } from 'sonner'`
|
||||
|
||||
**Loading States:**
|
||||
- Always add loading states, spinners, placeholder animations
|
||||
- Use skeletons until content renders
|
||||
|
||||
### Layout Implementation
|
||||
|
||||
**Spacing Strategy:**
|
||||
- Use grid/flex wrappers with `gap` for spacing
|
||||
- Prioritize wrappers over direct margins/padding on children
|
||||
- Nest wrappers as needed for complex layouts
|
||||
|
||||
**Conditional Styling:**
|
||||
- Use ternary operators or clsx/classnames utilities
|
||||
- Example: `className={clsx('base-class', { 'active-class': isActive })}`
|
||||
|
||||
### Responsive Design
|
||||
|
||||
**Fluid Layouts:**
|
||||
- Use relative units (%, em, rem) instead of fixed pixels
|
||||
- Implement CSS Grid and Flexbox for flexible layouts
|
||||
- Design mobile-first, then scale up
|
||||
|
||||
**Media Queries:**
|
||||
- Use breakpoints based on content needs, not specific devices
|
||||
- Test across range of devices and orientations
|
||||
|
||||
**Touch Targets:**
|
||||
- Minimum 44x44 pixels for interactive elements
|
||||
- Provide adequate spacing between touch targets
|
||||
- Consider hover states for desktop, focus states for touch/keyboard
|
||||
|
||||
**Performance:**
|
||||
- Optimize assets for mobile networks
|
||||
- Use CSS animations over JavaScript
|
||||
- Implement lazy loading for images and videos
|
||||
|
||||
## Accessibility Standards
|
||||
|
||||
**Core Requirements:**
|
||||
- Follow WCAG 2.1 AA guidelines
|
||||
- Ensure keyboard navigability for all interactive elements
|
||||
- Minimum touch target size: 44×44px
|
||||
- Use semantic HTML for screen reader compatibility
|
||||
- Provide alternative text for images and non-text content
|
||||
|
||||
**Implementation Details:**
|
||||
- Use descriptive variable and function names
|
||||
- Event functions: prefix with "handle" (handleClick, handleKeyDown)
|
||||
- Add accessibility attributes:
|
||||
- `tabindex="0"` for custom interactive elements
|
||||
- `aria-label` for buttons without text
|
||||
- `role` attributes when semantic HTML isn't sufficient
|
||||
- Ensure logical tab order
|
||||
- Provide visible focus states
|
||||
|
||||
## Design Process & Testing
|
||||
|
||||
### Design Workflow
|
||||
|
||||
1. **Understand Context:**
|
||||
- What problem are we solving?
|
||||
- Who are the users and when will they use this?
|
||||
- What are the success criteria?
|
||||
|
||||
2. **Explore Options:**
|
||||
- Present 2-3 alternative approaches
|
||||
- Explain trade-offs of each option
|
||||
- Ask which direction resonates
|
||||
|
||||
3. **Implement Iteratively:**
|
||||
- Start with structure and hierarchy
|
||||
- Add visual polish progressively
|
||||
- Test at each stage
|
||||
|
||||
4. **Validate:**
|
||||
- Use playwright MCP to test visual changes
|
||||
- Check across different screen sizes
|
||||
- Verify accessibility
|
||||
|
||||
### Testing Checklist
|
||||
|
||||
**Visual Testing:**
|
||||
- Use playwright MCP when available for automated testing
|
||||
- Check responsive behavior at common breakpoints
|
||||
- Verify touch targets on mobile
|
||||
- Test with different content lengths (short, long, edge cases)
|
||||
|
||||
**Accessibility Testing:**
|
||||
- Test keyboard navigation
|
||||
- Verify screen reader compatibility
|
||||
- Check color contrast ratios
|
||||
- Ensure focus states are visible
|
||||
|
||||
**Cross-Device Testing:**
|
||||
- Test on actual devices, not just emulators
|
||||
- Check different browsers (Chrome, Firefox, Safari)
|
||||
- Verify touch interactions on mobile
|
||||
- Test landscape and portrait orientations
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Designing a Button
|
||||
|
||||
**Initial Question to Ask:**
|
||||
```
|
||||
Before I implement this button, I have a few design questions:
|
||||
1. What's the primary action you want to emphasize?
|
||||
2. Do you have brand colors in mind, or should I suggest options?
|
||||
3. What emotion should this button convey? (urgent, calm, playful, professional)
|
||||
```
|
||||
|
||||
**After Getting Approval, Implementation:**
|
||||
```tsx
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { ArrowRight } from "@phosphor-icons/react";
|
||||
|
||||
<Button
|
||||
className="bg-slate-800 hover:bg-slate-700 text-white px-6 py-3 rounded-lg transition-colors duration-200"
|
||||
>
|
||||
Get Started
|
||||
<ArrowRight className="ml-2" />
|
||||
</Button>
|
||||
```
|
||||
|
||||
### Example 2: Typography Hierarchy
|
||||
|
||||
**Question First:**
|
||||
```
|
||||
For this content hierarchy, I'm thinking:
|
||||
- Headline: Large, bold, attention-grabbing
|
||||
- Subheading: Medium, regular weight
|
||||
- Body: Smaller, optimized for reading
|
||||
|
||||
Should we go bold and contemporary, or subtle and refined?
|
||||
```
|
||||
|
||||
**Implementation After Approval:**
|
||||
```tsx
|
||||
<div className="space-y-4">
|
||||
<h1 className="text-5xl font-bold tracking-tight text-slate-900">
|
||||
Headline Here
|
||||
</h1>
|
||||
<h2 className="text-2xl text-slate-600">
|
||||
Supporting subheading
|
||||
</h2>
|
||||
<p className="text-base leading-relaxed text-slate-700">
|
||||
Body text optimized for readability with proper line height.
|
||||
</p>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Example 3: Unique Color Palette Suggestion
|
||||
|
||||
**Presenting Options:**
|
||||
```
|
||||
I'd like to suggest three color directions that break from typical SaaS patterns:
|
||||
|
||||
1. **Warm Earth Tones**
|
||||
- Base: Warm grey (#E8E2DC)
|
||||
- Accent: Terracotta (#C86E4B)
|
||||
- For: Organic, trustworthy feel
|
||||
|
||||
2. **Cool Midnight**
|
||||
- Base: Deep navy (#1A2332)
|
||||
- Accent: Cyan (#4ECDC4)
|
||||
- For: Modern, tech-forward feel
|
||||
|
||||
3. **Soft Pastels**
|
||||
- Base: Soft pink (#FFE5E5)
|
||||
- Accent: Sage green (#9DB5A4)
|
||||
- For: Calm, approachable feel
|
||||
|
||||
Which direction feels right for your brand?
|
||||
```
|
||||
|
||||
## Common Patterns to Avoid
|
||||
|
||||
❌ **Don't:**
|
||||
- Use generic SaaS blue (#3B82F6) without considering alternatives
|
||||
- Default to shadows and gradients for depth
|
||||
- Copy Apple's design language
|
||||
- Use glass morphism effects
|
||||
- Make design decisions without asking
|
||||
- Implement typography without considering the font version
|
||||
- Use animations that delay user actions
|
||||
- Create cluttered interfaces with competing elements
|
||||
|
||||
✅ **Do:**
|
||||
- Ask before making design decisions
|
||||
- Suggest unique, contextually appropriate color pairs
|
||||
- Use flat, minimal design
|
||||
- Consider unconventional typography choices
|
||||
- Provide immediate feedback for interactions
|
||||
- Create generous white space
|
||||
- Test with real devices
|
||||
- Validate accessibility
|
||||
|
||||
## Version History
|
||||
|
||||
- v1.0.0 (2025-10-18): Initial release with comprehensive UI/UX design guidance
|
||||
|
||||
## References
|
||||
|
||||
For additional context, see:
|
||||
- WCAG 2.1 Guidelines: https://www.w3.org/WAI/WCAG21/quickref/
|
||||
- Google Fonts: https://fonts.google.com/
|
||||
- Tailwind CSS Docs: https://tailwindcss.com/docs
|
||||
- Shadcn UI Components: https://ui.shadcn.com/
|
||||
177
skills/frontend-design/LICENSE.txt
Normal file
177
skills/frontend-design/LICENSE.txt
Normal file
@@ -0,0 +1,177 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
42
skills/frontend-design/SKILL.md
Normal file
42
skills/frontend-design/SKILL.md
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
name: frontend-design
|
||||
description: Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, artifacts, posters, or applications (examples include websites, landing pages, dashboards, React components, HTML/CSS layouts, or when styling/beautifying any web UI). Generates creative, polished code and UI design that avoids generic AI aesthetics.
|
||||
license: Complete terms in LICENSE.txt
|
||||
---
|
||||
|
||||
This skill guides creation of distinctive, production-grade frontend interfaces that avoid generic "AI slop" aesthetics. Implement real working code with exceptional attention to aesthetic details and creative choices.
|
||||
|
||||
The user provides frontend requirements: a component, page, application, or interface to build. They may include context about the purpose, audience, or technical constraints.
|
||||
|
||||
## Design Thinking
|
||||
|
||||
Before coding, understand the context and commit to a BOLD aesthetic direction:
|
||||
- **Purpose**: What problem does this interface solve? Who uses it?
|
||||
- **Tone**: Pick an extreme: brutally minimal, maximalist chaos, retro-futuristic, organic/natural, luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian, etc. There are so many flavors to choose from. Use these for inspiration but design one that is true to the aesthetic direction.
|
||||
- **Constraints**: Technical requirements (framework, performance, accessibility).
|
||||
- **Differentiation**: What makes this UNFORGETTABLE? What's the one thing someone will remember?
|
||||
|
||||
**CRITICAL**: Choose a clear conceptual direction and execute it with precision. Bold maximalism and refined minimalism both work - the key is intentionality, not intensity.
|
||||
|
||||
Then implement working code (HTML/CSS/JS, React, Vue, etc.) that is:
|
||||
- Production-grade and functional
|
||||
- Visually striking and memorable
|
||||
- Cohesive with a clear aesthetic point-of-view
|
||||
- Meticulously refined in every detail
|
||||
|
||||
## Frontend Aesthetics Guidelines
|
||||
|
||||
Focus on:
|
||||
- **Typography**: Choose fonts that are beautiful, unique, and interesting. Avoid generic fonts like Arial and Inter; opt instead for distinctive choices that elevate the frontend's aesthetics; unexpected, characterful font choices. Pair a distinctive display font with a refined body font.
|
||||
- **Color & Theme**: Commit to a cohesive aesthetic. Use CSS variables for consistency. Dominant colors with sharp accents outperform timid, evenly-distributed palettes.
|
||||
- **Motion**: Use animations for effects and micro-interactions. Prioritize CSS-only solutions for HTML. Use Motion library for React when available. Focus on high-impact moments: one well-orchestrated page load with staggered reveals (animation-delay) creates more delight than scattered micro-interactions. Use scroll-triggering and hover states that surprise.
|
||||
- **Spatial Composition**: Unexpected layouts. Asymmetry. Overlap. Diagonal flow. Grid-breaking elements. Generous negative space OR controlled density.
|
||||
- **Backgrounds & Visual Details**: Create atmosphere and depth rather than defaulting to solid colors. Add contextual effects and textures that match the overall aesthetic. Apply creative forms like gradient meshes, noise textures, geometric patterns, layered transparencies, dramatic shadows, decorative borders, custom cursors, and grain overlays.
|
||||
|
||||
NEVER use generic AI-generated aesthetics like overused font families (Inter, Roboto, Arial, system fonts), cliched color schemes (particularly purple gradients on white backgrounds), predictable layouts and component patterns, and cookie-cutter design that lacks context-specific character.
|
||||
|
||||
Interpret creatively and make unexpected choices that feel genuinely designed for the context. No design should be the same. Vary between light and dark themes, different fonts, different aesthetics. NEVER converge on common choices (Space Grotesk, for example) across generations.
|
||||
|
||||
**IMPORTANT**: Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate code with extensive animations and effects. Minimalist or refined designs need restraint, precision, and careful attention to spacing, typography, and subtle details. Elegance comes from executing the vision well.
|
||||
|
||||
Remember: Claude is capable of extraordinary creative work. Don't hold back, show what can truly be created when thinking outside the box and committing fully to a distinctive vision.
|
||||
111
skills/innovative-ux-designer/ACCESSIBILITY.md
Normal file
111
skills/innovative-ux-designer/ACCESSIBILITY.md
Normal file
@@ -0,0 +1,111 @@
|
||||
# Accessibility Essentials
|
||||
|
||||
Accessibility enables creativity - it's a foundation, not a limitation. WCAG 2.1 AA compliance.
|
||||
|
||||
## Core Principles (POUR)
|
||||
|
||||
- **Perceivable**: Content must be perceivable (alt text, contrast, captions)
|
||||
- **Operable**: UI must be keyboard/touch accessible
|
||||
- **Understandable**: Clear, predictable behavior
|
||||
- **Robust**: Works with assistive technologies
|
||||
|
||||
## Contrast Requirements
|
||||
|
||||
| Element | Minimum Ratio |
|
||||
|---------|---------------|
|
||||
| Normal text | 4.5:1 |
|
||||
| Large text (18pt+) | 3:1 |
|
||||
| UI components | 3:1 |
|
||||
|
||||
**Tools**: Chrome DevTools Accessibility tab, WebAIM Contrast Checker
|
||||
|
||||
## Keyboard Navigation
|
||||
|
||||
```tsx
|
||||
// All interactive elements need focus states
|
||||
<button className="focus:ring-4 focus:ring-blue-500 focus:outline-none">
|
||||
Accessible
|
||||
</button>
|
||||
|
||||
// Custom elements need tabindex and key handlers
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onKeyDown={(e) => (e.key === 'Enter' || e.key === ' ') && handleClick()}
|
||||
>
|
||||
Custom Button
|
||||
</div>
|
||||
```
|
||||
|
||||
**Essentials:**
|
||||
- Tab through entire interface
|
||||
- Enter/Space activates elements
|
||||
- Escape closes modals
|
||||
- Visible focus indicators always
|
||||
|
||||
## Essential ARIA
|
||||
|
||||
```tsx
|
||||
// Buttons without text
|
||||
<button aria-label="Close dialog"><X /></button>
|
||||
|
||||
// Expandable elements
|
||||
<button aria-expanded={isOpen} aria-controls="menu">Menu</button>
|
||||
|
||||
// Live regions for dynamic content
|
||||
<div role="status" aria-live="polite">{statusMessage}</div>
|
||||
<div role="alert" aria-live="assertive">{errorMessage}</div>
|
||||
|
||||
// Form errors
|
||||
<input aria-invalid={hasError} aria-describedby="error-msg" />
|
||||
{hasError && <p id="error-msg" role="alert">Error text</p>}
|
||||
```
|
||||
|
||||
## Semantic HTML
|
||||
|
||||
```tsx
|
||||
// Use semantic elements, not divs
|
||||
<header><nav>...</nav></header>
|
||||
<main><article><h1>...</h1></article></main>
|
||||
<footer>...</footer>
|
||||
|
||||
// Heading hierarchy (never skip levels)
|
||||
<h1>Page Title</h1>
|
||||
<h2>Section</h2>
|
||||
<h3>Subsection</h3>
|
||||
```
|
||||
|
||||
## Touch Targets
|
||||
|
||||
- Minimum **44x44px** for all interactive elements
|
||||
- Adequate spacing between targets
|
||||
- `touch-manipulation` CSS for responsive touch
|
||||
|
||||
## Screen Reader Content
|
||||
|
||||
```tsx
|
||||
// Hidden but announced
|
||||
<span className="sr-only">Additional context</span>
|
||||
|
||||
// Skip link
|
||||
<a href="#main" className="sr-only focus:not-sr-only">
|
||||
Skip to main content
|
||||
</a>
|
||||
```
|
||||
|
||||
## Quick Checklist
|
||||
|
||||
- [ ] Keyboard: Can tab through everything
|
||||
- [ ] Focus: Visible focus indicators
|
||||
- [ ] Contrast: 4.5:1 for text
|
||||
- [ ] Alt text: All images have appropriate alt
|
||||
- [ ] Headings: Logical h1-h6 hierarchy
|
||||
- [ ] Forms: Labels associated with inputs
|
||||
- [ ] Errors: Announced to screen readers
|
||||
- [ ] Touch: 44px minimum targets
|
||||
|
||||
## Resources
|
||||
|
||||
- [WCAG 2.1 Quick Reference](https://www.w3.org/WAI/WCAG21/quickref/)
|
||||
- [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/)
|
||||
- [ARIA Authoring Practices](https://www.w3.org/WAI/ARIA/apg/)
|
||||
577
skills/innovative-ux-designer/DESIGN-SYSTEM-TEMPLATE.md
Normal file
577
skills/innovative-ux-designer/DESIGN-SYSTEM-TEMPLATE.md
Normal file
@@ -0,0 +1,577 @@
|
||||
# Design System Template
|
||||
|
||||
Meta-framework for understanding what's fixed, project-specific, and adaptable in your design system.
|
||||
|
||||
## Purpose
|
||||
|
||||
This template helps you distinguish between:
|
||||
- **Fixed Elements**: Universal rules that never change
|
||||
- **Project-Specific Elements**: Filled in for each project based on brand
|
||||
- **Adaptable Elements**: Context-dependent implementations
|
||||
|
||||
---
|
||||
|
||||
## I. FIXED ELEMENTS
|
||||
|
||||
These foundations remain consistent across all projects, regardless of brand or context.
|
||||
|
||||
### 1. Spacing Scale
|
||||
|
||||
**Fixed System:**
|
||||
```
|
||||
4px, 8px, 12px, 16px, 24px, 32px, 48px, 64px, 96px
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
- Margins, padding, gaps between elements
|
||||
- Mathematical relationships ensure visual harmony
|
||||
- Use multipliers of base unit (4px)
|
||||
|
||||
**Why Fixed:**
|
||||
Consistent spacing creates visual rhythm regardless of brand personality.
|
||||
|
||||
### 2. Grid System
|
||||
|
||||
**Fixed Structure:**
|
||||
- **12-column grid** for most layouts (divisible by 2, 3, 4, 6)
|
||||
- **16-column grid** for data-heavy interfaces
|
||||
- **Gutters**: 16px (mobile), 24px (tablet), 32px (desktop)
|
||||
|
||||
**Why Fixed:**
|
||||
Grid provides structural order. Brand personality shows through color, typography, content—not grid structure.
|
||||
|
||||
### 3. Accessibility Standards
|
||||
|
||||
**Fixed Requirements:**
|
||||
- **WCAG 2.1 AA** compliance minimum
|
||||
- **Contrast**: 4.5:1 for normal text, 3:1 for large text
|
||||
- **Touch targets**: Minimum 44×44px
|
||||
- **Keyboard navigation**: All interactive elements accessible
|
||||
- **Screen reader**: Semantic HTML, ARIA labels where needed
|
||||
|
||||
**Why Fixed:**
|
||||
Accessibility is not negotiable. It's a baseline requirement for ethical, legal, and usable products.
|
||||
|
||||
### 4. Typography Hierarchy Logic
|
||||
|
||||
**Fixed Structure:**
|
||||
- **Mathematical scaling**: 1.25x (major third) or 1.333x (perfect fourth)
|
||||
- **Hierarchy levels**: Display → H1 → H2 → H3 → Body → Small → Caption
|
||||
- **Line height**: 1.5x for body text, 1.2-1.3x for headlines
|
||||
- **Line length**: 45-75 characters optimal
|
||||
|
||||
**Why Fixed:**
|
||||
Mathematical relationships create predictable, harmonious hierarchy. Specific fonts change, but the logic doesn't.
|
||||
|
||||
### 5. Component Architecture
|
||||
|
||||
**Fixed Patterns:**
|
||||
- **Button states**: Default, Hover, Active, Focus, Disabled
|
||||
- **Form structure**: Label above input, error below, helper text optional
|
||||
- **Modal pattern**: Overlay + centered content + close mechanism
|
||||
- **Card structure**: Container → Header → Body → Footer (optional)
|
||||
|
||||
**Why Fixed:**
|
||||
Users expect consistent component behavior. Architecture is fixed; appearance is project-specific.
|
||||
|
||||
### 6. Animation Timing Framework
|
||||
|
||||
**Fixed Physics Profiles:**
|
||||
- **Lightweight** (icons, chips): 150ms
|
||||
- **Standard** (cards, panels): 300ms
|
||||
- **Weighty** (modals, pages): 500ms
|
||||
|
||||
**Fixed Easing:**
|
||||
- **Ease-out**: Entrances (fast start, slow end)
|
||||
- **Ease-in**: Exits (slow start, fast end)
|
||||
- **Ease-in-out**: Transitions (smooth both ends)
|
||||
|
||||
**Why Fixed:**
|
||||
Natural physics feel consistent across brands. Duration and easing create that feeling.
|
||||
|
||||
---
|
||||
|
||||
## II. PROJECT-SPECIFIC ELEMENTS
|
||||
|
||||
Fill in these for each project based on brand personality and purpose.
|
||||
|
||||
### 1. Brand Color System
|
||||
|
||||
**Template Structure:**
|
||||
|
||||
```
|
||||
NEUTRALS (4-5 colors):
|
||||
- Background lightest: _______ (e.g., slate-50 or warm-white)
|
||||
- Surface: _______ (e.g., slate-100)
|
||||
- Border/divider: _______ (e.g., slate-300)
|
||||
- Text secondary: _______ (e.g., slate-600)
|
||||
- Text primary: _______ (e.g., slate-900)
|
||||
|
||||
ACCENTS (1-3 colors):
|
||||
- Primary (main CTA): _______ (e.g., teal-500)
|
||||
- Secondary (alternative action): _______ (optional)
|
||||
- Status colors:
|
||||
- Success: _______ (green-ish)
|
||||
- Warning: _______ (amber-ish)
|
||||
- Error: _______ (red-ish)
|
||||
- Info: _______ (blue-ish)
|
||||
```
|
||||
|
||||
**Questions to Answer:**
|
||||
- What emotion should the brand evoke? (Trust, excitement, calm, urgency)
|
||||
- Warm or cool neutrals?
|
||||
- Conservative or bold accents?
|
||||
|
||||
**Examples:**
|
||||
|
||||
**Project A: Fintech App**
|
||||
```
|
||||
Neutrals: Cool greys (slate-50 → slate-900)
|
||||
Primary: Deep blue (#0A2463) – trust, professionalism
|
||||
Success: Muted green (#10B981)
|
||||
Why: Financial products need trust, not playfulness
|
||||
```
|
||||
|
||||
**Project B: Creative Community**
|
||||
```
|
||||
Neutrals: Warm greys with beige undertones
|
||||
Primary: Coral (#FF6B6B) – energy, creativity
|
||||
Success: Teal (#06D6A0) – fresh, unexpected
|
||||
Why: Creative spaces should feel inviting, not corporate
|
||||
```
|
||||
|
||||
**Project C: Healthcare Platform**
|
||||
```
|
||||
Neutrals: Pure greys (minimal color temperature)
|
||||
Primary: Soft blue (#4A90E2) – calm, clinical
|
||||
Success: Medical green (#38A169)
|
||||
Why: Healthcare needs clarity and calm, not distraction
|
||||
```
|
||||
|
||||
### 2. Typography Pairing
|
||||
|
||||
**Template:**
|
||||
|
||||
```
|
||||
HEADLINE FONT: _______
|
||||
- Weight: _______ (e.g., Bold 700)
|
||||
- Use case: H1, H2, display text
|
||||
- Personality: _______ (geometric/humanist/serif/etc.)
|
||||
|
||||
BODY FONT: _______
|
||||
- Weight: _______ (e.g., Regular 400, Medium 500)
|
||||
- Use case: Paragraphs, UI text
|
||||
- Personality: _______ (neutral/readable/efficient)
|
||||
|
||||
OPTIONAL ACCENT FONT: _______
|
||||
- Weight: _______
|
||||
- Use case: _______ (special headlines, callouts)
|
||||
```
|
||||
|
||||
**Pairing Logic:**
|
||||
- Serif + Sans-serif (classic, editorial)
|
||||
- Geometric + Humanist (modern + warm)
|
||||
- Display + System (distinctive + efficient)
|
||||
|
||||
**Examples:**
|
||||
|
||||
**Project A: Editorial Platform**
|
||||
```
|
||||
Headline: Playfair Display (Serif, Bold 700)
|
||||
Body: Inter (Sans-serif, Regular 400)
|
||||
Why: Serif headlines = trustworthy, editorial feel
|
||||
```
|
||||
|
||||
**Project B: Tech Startup**
|
||||
```
|
||||
Headline: DM Sans (Sans-serif, Bold 700)
|
||||
Body: DM Sans (Regular 400, Medium 500)
|
||||
Why: Single-font system = modern, efficient, cohesive
|
||||
```
|
||||
|
||||
**Project C: Luxury Brand**
|
||||
```
|
||||
Headline: Cormorant Garamond (Serif, Light 300)
|
||||
Body: Lato (Sans-serif, Regular 400)
|
||||
Why: Elegant serif + readable sans = sophisticated
|
||||
```
|
||||
|
||||
### 3. Tone of Voice
|
||||
|
||||
**Template:**
|
||||
|
||||
```
|
||||
BRAND PERSONALITY:
|
||||
- Formal ↔ Casual: _______ (1-10 scale)
|
||||
- Professional ↔ Friendly: _______ (1-10 scale)
|
||||
- Serious ↔ Playful: _______ (1-10 scale)
|
||||
- Authoritative ↔ Conversational: _______ (1-10 scale)
|
||||
|
||||
MICROCOPY EXAMPLES:
|
||||
- Button label (submit form): _______
|
||||
- Error message (invalid email): _______
|
||||
- Success message (saved): _______
|
||||
- Empty state: _______
|
||||
|
||||
ANIMATION PERSONALITY:
|
||||
- Speed: _______ (quick/moderate/slow)
|
||||
- Feel: _______ (precise/smooth/bouncy)
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
|
||||
**Project A: Banking App**
|
||||
```
|
||||
Personality: Formal (8), Professional (9), Serious (8)
|
||||
Button: "Submit Application"
|
||||
Error: "Email address format is invalid"
|
||||
Success: "Application submitted successfully"
|
||||
Animation: Quick (precise, efficient, no-nonsense)
|
||||
```
|
||||
|
||||
**Project B: Social App**
|
||||
```
|
||||
Personality: Casual (8), Friendly (9), Playful (7)
|
||||
Button: "Let's go!"
|
||||
Error: "Hmm, that email doesn't look right"
|
||||
Success: "Nice! You're all set 🎉"
|
||||
Animation: Moderate (smooth, friendly bounce)
|
||||
```
|
||||
|
||||
### 4. Animation Speed & Feel
|
||||
|
||||
**Template:**
|
||||
|
||||
```
|
||||
SPEED PREFERENCE:
|
||||
- UI interactions: _______ (100-150ms / 150-200ms / 200-300ms)
|
||||
- State changes: _______ (200ms / 300ms / 400ms)
|
||||
- Page transitions: _______ (300ms / 500ms / 700ms)
|
||||
|
||||
ANIMATION STYLE:
|
||||
- Easing preference: _______ (sharp / standard / bouncy)
|
||||
- Movement type: _______ (minimal / smooth / expressive)
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
|
||||
**Project A: Trading Platform**
|
||||
```
|
||||
Speed: Fast (100ms UI, 200ms states, 300ms pages)
|
||||
Style: Sharp easing, minimal movement
|
||||
Why: Traders need speed, not distraction
|
||||
```
|
||||
|
||||
**Project B: Wellness App**
|
||||
```
|
||||
Speed: Slow (200ms UI, 400ms states, 500ms pages)
|
||||
Style: Smooth easing, gentle movement
|
||||
Why: Calm, relaxing experience matches brand
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## III. ADAPTABLE ELEMENTS
|
||||
|
||||
Context-dependent implementations that vary based on use case.
|
||||
|
||||
### 1. Component Variations
|
||||
|
||||
**Button Variants:**
|
||||
- **Primary**: Full background color (high emphasis)
|
||||
- **Secondary**: Outline only (medium emphasis)
|
||||
- **Tertiary**: Text only (low emphasis)
|
||||
- **Destructive**: Red-ish (danger actions)
|
||||
- **Ghost**: Minimal (navigation, toolbars)
|
||||
|
||||
**Adaptation Rules:**
|
||||
- Primary: Main CTA, one per screen section
|
||||
- Secondary: Alternative actions
|
||||
- Tertiary: Less important actions, multiple allowed
|
||||
- Use brand colors, but hierarchy logic is fixed
|
||||
|
||||
### 2. Responsive Breakpoints
|
||||
|
||||
**Fixed Ranges:**
|
||||
- XS: 0-479px (small phones)
|
||||
- SM: 480-767px (large phones)
|
||||
- MD: 768-1023px (tablets)
|
||||
- LG: 1024-1439px (laptops)
|
||||
- XL: 1440px+ (desktop)
|
||||
|
||||
**Adaptable Implementations:**
|
||||
|
||||
**Simple Content Site:**
|
||||
```
|
||||
XS-SM: Single column
|
||||
MD: 2 columns
|
||||
LG-XL: 3 columns max
|
||||
Why: Content-focused, don't overwhelm
|
||||
```
|
||||
|
||||
**Dashboard/Data App:**
|
||||
```
|
||||
XS: Collapsed, cards stack
|
||||
SM: Simplified sidebar
|
||||
MD: Full sidebar + main content
|
||||
LG-XL: Sidebar + main + right panel
|
||||
Why: Data apps need more screen real estate
|
||||
```
|
||||
|
||||
### 3. Dark Mode Palette
|
||||
|
||||
**Adaptation Strategy:**
|
||||
|
||||
Not a simple inversion. Dark mode needs adjusted contrast:
|
||||
|
||||
**Light Mode:**
|
||||
```
|
||||
Background: #FFFFFF (white)
|
||||
Text: #0F172A (slate-900) → 21:1 contrast
|
||||
```
|
||||
|
||||
**Dark Mode (Adapted):**
|
||||
```
|
||||
Background: #0F172A (slate-900)
|
||||
Text: #E2E8F0 (slate-200) → 15.8:1 contrast (still AA, but softer)
|
||||
```
|
||||
|
||||
**Why Adapt:**
|
||||
Pure white on pure black is too harsh. Dark mode needs slightly lower contrast for eye comfort.
|
||||
|
||||
### 4. Loading States
|
||||
|
||||
**Context-Dependent:**
|
||||
|
||||
**Fast operations (<500ms):**
|
||||
- No loading indicator (feels instant)
|
||||
|
||||
**Medium operations (500ms-2s):**
|
||||
- Spinner or skeleton screen
|
||||
|
||||
**Long operations (>2s):**
|
||||
- Progress bar with percentage
|
||||
- Or: Skeleton + estimated time
|
||||
|
||||
**Interactive Operations:**
|
||||
- Button shows spinner inside (don't disable, show state)
|
||||
|
||||
### 5. Error Handling Strategy
|
||||
|
||||
**Context-Dependent:**
|
||||
|
||||
**Form Errors:**
|
||||
```
|
||||
Validate: On blur (after user leaves field)
|
||||
Display: Inline below field
|
||||
Recovery: Clear error on fix
|
||||
```
|
||||
|
||||
**API Errors:**
|
||||
```
|
||||
Transient (network): Show retry button
|
||||
Permanent (404): Show helpful message + next steps
|
||||
Critical (500): Contact support option
|
||||
```
|
||||
|
||||
**Data Errors:**
|
||||
```
|
||||
Missing: Show empty state with action
|
||||
Corrupt: Show error boundary with reload
|
||||
Invalid: Highlight + explain what's wrong
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## DECISION TREE
|
||||
|
||||
When implementing a feature, ask:
|
||||
|
||||
### Is this...
|
||||
|
||||
**FIXED?**
|
||||
- Does it affect structure, accessibility, or universal UX?
|
||||
- Examples: Spacing scale, grid, contrast ratios, component architecture
|
||||
- **Action**: Use the fixed system, no variation
|
||||
|
||||
**PROJECT-SPECIFIC?**
|
||||
- Does it express brand personality or purpose?
|
||||
- Examples: Colors, typography, tone of voice, animation feel
|
||||
- **Action**: Fill in the template for this project
|
||||
|
||||
**ADAPTABLE?**
|
||||
- Does it depend on context, content, or use case?
|
||||
- Examples: Component variants, responsive behavior, error handling
|
||||
- **Action**: Choose appropriate variation based on context
|
||||
|
||||
---
|
||||
|
||||
## EXAMPLE: Implementing a "Submit" Button
|
||||
|
||||
### Fixed Elements (Always the same):
|
||||
- Touch target: 44px minimum height
|
||||
- Padding: 16px horizontal (from spacing scale)
|
||||
- States: Default, Hover, Active, Focus, Disabled
|
||||
- Animation: 150ms ease-out (lightweight profile)
|
||||
|
||||
### Project-Specific (Filled per project):
|
||||
- **Project A (Bank)**: Dark blue background, white text, "Submit Application"
|
||||
- **Project B (Social)**: Coral background, white text, "Let's Go!"
|
||||
- **Project C (Healthcare)**: Soft blue background, white text, "Continue"
|
||||
|
||||
### Adaptable (Context-dependent):
|
||||
- **Form context**: Primary button (full color)
|
||||
- **Toolbar context**: Ghost button (text only)
|
||||
- **Danger context**: Destructive variant (red-ish)
|
||||
|
||||
---
|
||||
|
||||
## VALIDATION CHECKLIST
|
||||
|
||||
Before finalizing a design, check:
|
||||
|
||||
### Fixed Elements
|
||||
- [ ] Uses spacing scale (4/8/12/16/24/32/48/64/96px)
|
||||
- [ ] Follows grid system (12 or 16 columns)
|
||||
- [ ] Meets WCAG AA contrast (4.5:1 normal, 3:1 large)
|
||||
- [ ] Touch targets ≥ 44px
|
||||
- [ ] Typography follows mathematical scale
|
||||
- [ ] Components follow standard architecture
|
||||
|
||||
### Project-Specific Elements
|
||||
- [ ] Brand colors filled in and intentional
|
||||
- [ ] Typography pairing chosen and justified
|
||||
- [ ] Tone of voice defined and consistent
|
||||
- [ ] Animation speed matches brand personality
|
||||
|
||||
### Adaptable Elements
|
||||
- [ ] Component variants appropriate for context
|
||||
- [ ] Responsive behavior fits content type
|
||||
- [ ] Loading states match operation duration
|
||||
- [ ] Error handling fits error type
|
||||
|
||||
---
|
||||
|
||||
## PROJECT KICKOFF TEMPLATE
|
||||
|
||||
Use this to start a new project:
|
||||
|
||||
```
|
||||
PROJECT NAME: _______________________
|
||||
PURPOSE: ____________________________
|
||||
|
||||
BRAND PERSONALITY:
|
||||
- Primary emotion: _______
|
||||
- Warm or cool: _______
|
||||
- Formal or casual: _______
|
||||
- Conservative or bold: _______
|
||||
|
||||
COLORS (fill the template):
|
||||
- Neutral base: _______
|
||||
- Primary accent: _______
|
||||
- Status colors: _______ / _______ / _______
|
||||
|
||||
TYPOGRAPHY (fill the template):
|
||||
- Headline font: _______
|
||||
- Body font: _______
|
||||
- Pairing rationale: _______
|
||||
|
||||
TONE:
|
||||
- Button labels style: _______
|
||||
- Error message style: _______
|
||||
- Success message style: _______
|
||||
|
||||
ANIMATION:
|
||||
- Speed preference: _______ (fast/moderate/slow)
|
||||
- Feel preference: _______ (sharp/smooth/bouncy)
|
||||
|
||||
TARGET DEVICES:
|
||||
- Primary: _______ (mobile/desktop/both)
|
||||
- Secondary: _______
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## MAINTAINING CONSISTENCY
|
||||
|
||||
### Documentation
|
||||
- Keep this template updated as system evolves
|
||||
- Document WHY choices were made, not just WHAT
|
||||
|
||||
### Communication
|
||||
- Share with designers: "Here's what varies vs. what's fixed"
|
||||
- Share with developers: "Here are the design tokens"
|
||||
|
||||
### Tooling
|
||||
- Use CSS variables for project-specific values
|
||||
- Use Tailwind config for spacing scale
|
||||
- Use design tokens in Figma/Storybook
|
||||
|
||||
### Reviews
|
||||
- Audit: Does new work follow fixed elements?
|
||||
- Validate: Are project-specific elements intentional?
|
||||
- Question: Are adaptations justified by context?
|
||||
|
||||
---
|
||||
|
||||
## EXAMPLES OF COMPLETE SYSTEMS
|
||||
|
||||
### System A: B2B SaaS (Conservative)
|
||||
|
||||
**Fixed**: Standard spacing, 12-col grid, WCAG AA, major third type scale
|
||||
**Project-Specific**:
|
||||
- Colors: Cool greys + corporate blue
|
||||
- Typography: DM Sans (headlines + body)
|
||||
- Tone: Professional, formal
|
||||
- Animation: Quick, precise (150ms)
|
||||
**Adaptable**:
|
||||
- Dashboard gets multi-panel layout
|
||||
- Forms are extensive (use progressive disclosure)
|
||||
- Errors show detailed technical info
|
||||
|
||||
### System B: Consumer Social App (Playful)
|
||||
|
||||
**Fixed**: Same spacing/grid/accessibility/type logic
|
||||
**Project-Specific**:
|
||||
- Colors: Warm greys + vibrant coral
|
||||
- Typography: Poppins (headlines) + Inter (body)
|
||||
- Tone: Casual, friendly, playful
|
||||
- Animation: Moderate, bouncy (200ms)
|
||||
**Adaptable**:
|
||||
- Mobile-first (most users on phones)
|
||||
- Forms are minimal (progressive profiling)
|
||||
- Errors are friendly, not technical
|
||||
|
||||
### System C: Healthcare Platform (Clinical)
|
||||
|
||||
**Fixed**: Same foundational structure
|
||||
**Project-Specific**:
|
||||
- Colors: Pure greys + medical blue
|
||||
- Typography: System fonts (SF Pro / Segoe)
|
||||
- Tone: Clear, authoritative, calm
|
||||
- Animation: Slow, smooth (300ms)
|
||||
**Adaptable**:
|
||||
- Desktop-first (clinical use at workstations)
|
||||
- Forms are complex (HIPAA compliance)
|
||||
- Errors are precise with next steps
|
||||
|
||||
---
|
||||
|
||||
## KEY TAKEAWAY
|
||||
|
||||
**The system flexibility framework lets you:**
|
||||
- Maintain consistency (fixed elements)
|
||||
- Express brand personality (project-specific)
|
||||
- Adapt to context (adaptable elements)
|
||||
|
||||
**Without this framework:**
|
||||
- Designers reinvent spacing every project
|
||||
- Components feel inconsistent across products
|
||||
- Brand personality overrides accessibility
|
||||
- Context-blind implementations feel wrong
|
||||
|
||||
**With this framework:**
|
||||
- Speed: Start from proven foundations
|
||||
- Consistency: Fixed elements guarantee it
|
||||
- Flexibility: Express unique brand identity
|
||||
- Context: Adapt without breaking system
|
||||
72
skills/innovative-ux-designer/MOTION-SPEC.md
Normal file
72
skills/innovative-ux-designer/MOTION-SPEC.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# Motion Specification
|
||||
|
||||
Motion should surprise and delight while serving function. Animation is a creative tool.
|
||||
|
||||
## Easing Curves
|
||||
|
||||
| Easing | CSS | Use For |
|
||||
|--------|-----|---------|
|
||||
| **Ease-out** | `cubic-bezier(0.0, 0.0, 0.2, 1)` | Entrances, appearing |
|
||||
| **Ease-in** | `cubic-bezier(0.4, 0.0, 1, 1)` | Exits, disappearing |
|
||||
| **Ease-in-out** | `cubic-bezier(0.4, 0.0, 0.2, 1)` | State changes, transforms |
|
||||
| **Spring** | `cubic-bezier(0.68, -0.55, 0.265, 1.55)` | Playful, attention-grabbing |
|
||||
| **Linear** | `linear` | Spinners, continuous loops |
|
||||
|
||||
## Duration by Element Weight
|
||||
|
||||
| Weight | Duration | Examples |
|
||||
|--------|----------|----------|
|
||||
| **Lightweight** | 150ms | Icons, badges, chips |
|
||||
| **Standard** | 300ms | Cards, panels, list items |
|
||||
| **Weighty** | 500ms | Modals, page transitions |
|
||||
|
||||
## Duration by Interaction
|
||||
|
||||
| Interaction | Duration |
|
||||
|-------------|----------|
|
||||
| Button press | 100ms |
|
||||
| Hover state | 150ms |
|
||||
| Tooltip appear | 200ms |
|
||||
| Tab switch | 250ms |
|
||||
| Modal open | 300ms |
|
||||
| Page transition | 400ms |
|
||||
|
||||
## Common Patterns
|
||||
|
||||
```tsx
|
||||
// Hover transition (CSS)
|
||||
<button className="transition-colors duration-150 ease-out hover:bg-blue-700">
|
||||
|
||||
// Fade + slide (Framer Motion)
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.3, ease: "easeOut" }}
|
||||
/>
|
||||
|
||||
// Stagger children
|
||||
<motion.ul variants={{ visible: { transition: { staggerChildren: 0.1 } } }}>
|
||||
<motion.li variants={{ hidden: { opacity: 0 }, visible: { opacity: 1 } }} />
|
||||
</motion.ul>
|
||||
```
|
||||
|
||||
## Performance Rules
|
||||
|
||||
- Only animate `transform` and `opacity` (GPU-accelerated)
|
||||
- Avoid animating `width`, `height`, `margin`, `padding`
|
||||
- Keep durations under 500ms for UI interactions
|
||||
- Respect `prefers-reduced-motion`:
|
||||
|
||||
```css
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*, *::before, *::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- [Framer Motion](https://www.framer.com/motion/)
|
||||
- [CSS Easing Functions](https://easings.net/)
|
||||
90
skills/innovative-ux-designer/RESPONSIVE-DESIGN.md
Normal file
90
skills/innovative-ux-designer/RESPONSIVE-DESIGN.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# Responsive Design Essentials
|
||||
|
||||
Mobile-first approach: start with mobile, progressively enhance for larger screens.
|
||||
|
||||
## Breakpoints
|
||||
|
||||
| Range | Pixels | Devices | Strategy |
|
||||
|-------|--------|---------|----------|
|
||||
| **XS** | 0-479px | Small phones | Single column, stacked nav, 44px touch targets |
|
||||
| **SM** | 480-767px | Large phones | Single column, bottom nav, simplified UI |
|
||||
| **MD** | 768-1023px | Tablets | 2 columns possible, sidebar nav |
|
||||
| **LG** | 1024-1439px | Laptops | Multi-column, full nav, desktop UI |
|
||||
| **XL** | 1440px+ | Desktop | Max-width containers, multi-panel layouts |
|
||||
|
||||
## Tailwind Responsive
|
||||
|
||||
```tsx
|
||||
// Mobile-first: base styles, then scale up
|
||||
<div className="
|
||||
w-full // mobile: full width
|
||||
sm:w-1/2 // 480px+: half
|
||||
md:w-1/3 // 768px+: third
|
||||
lg:w-1/4 // 1024px+: quarter
|
||||
">
|
||||
|
||||
// Responsive grid
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
|
||||
|
||||
// Responsive typography
|
||||
<h1 className="text-3xl md:text-4xl lg:text-5xl">
|
||||
|
||||
// Show/hide by breakpoint
|
||||
<div className="block md:hidden">Mobile only</div>
|
||||
<div className="hidden md:block">Desktop only</div>
|
||||
```
|
||||
|
||||
## Fluid Typography
|
||||
|
||||
```css
|
||||
h1 { font-size: clamp(2rem, 5vw, 4rem); }
|
||||
p { font-size: clamp(1rem, 2.5vw, 1.25rem); }
|
||||
```
|
||||
|
||||
## Touch Targets
|
||||
|
||||
- Minimum **44x44px** for all interactive elements
|
||||
- Use `touch-manipulation` to prevent 300ms tap delay
|
||||
- Adequate spacing between targets
|
||||
|
||||
```tsx
|
||||
<button className="min-w-[44px] min-h-[44px] touch-manipulation">
|
||||
```
|
||||
|
||||
## Mobile Simplification
|
||||
|
||||
| Desktop | Mobile |
|
||||
|---------|--------|
|
||||
| Full nav bar | Hamburger menu |
|
||||
| Side-by-side fields | Stacked fields |
|
||||
| Multi-column grid | Single column |
|
||||
| Inline buttons | Fixed bottom bar |
|
||||
| Data table | Collapsed cards |
|
||||
| Visible sidebar | Hidden/collapsible |
|
||||
|
||||
## Images
|
||||
|
||||
```tsx
|
||||
// Responsive images
|
||||
<img
|
||||
srcSet="image-400w.jpg 400w, image-800w.jpg 800w, image-1200w.jpg 1200w"
|
||||
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
|
||||
loading="lazy"
|
||||
/>
|
||||
|
||||
// Next.js
|
||||
<Image src="/hero.jpg" width={1200} height={600} priority className="w-full h-auto" />
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
Test at these widths:
|
||||
- 375px (iPhone SE)
|
||||
- 390px (iPhone 14)
|
||||
- 768px (iPad)
|
||||
- 1024px (iPad Pro)
|
||||
- 1280px+ (Desktop)
|
||||
|
||||
## Resources
|
||||
|
||||
- [Tailwind Responsive](https://tailwindcss.com/docs/responsive-design)
|
||||
718
skills/innovative-ux-designer/SKILL.md
Normal file
718
skills/innovative-ux-designer/SKILL.md
Normal file
@@ -0,0 +1,718 @@
|
||||
---
|
||||
name: innovative-ux-designer
|
||||
description: Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
|
||||
metadata:
|
||||
version: 2.0.0
|
||||
---
|
||||
|
||||
# Innovative UX Designer
|
||||
|
||||
Create distinctive, production-grade frontend interfaces that avoid generic "AI slop" aesthetics. Implement real working code with exceptional attention to aesthetic details and creative choices. Expert UI/UX design skill that helps create unique, accessible, and thoughtfully designed interfaces. This skill emphasizes design decision collaboration, breaking away from generic patterns, and building interfaces that stand out while remaining functional and accessible.
|
||||
|
||||
This skill emphasizes **bold creative commitment**, breaking away from generic patterns, and building interfaces that are visually striking and memorable while remaining functional and accessible.
|
||||
|
||||
## Core Philosophy
|
||||
|
||||
**CRITICAL: Design Thinking Protocol**
|
||||
|
||||
Before coding, **ASK to understand context**, then **COMMIT BOLDLY** to a distinctive direction:
|
||||
|
||||
### Questions to Ask First
|
||||
1. **Purpose**: What problem does this interface solve? Who uses it?
|
||||
2. **Tone**: What aesthetic extreme fits? (see Tone Options below)
|
||||
3. **Constraints**: Technical requirements (framework, performance, accessibility)?
|
||||
4. **Differentiation**: What makes this UNFORGETTABLE? What's the one thing someone will remember?
|
||||
|
||||
### Tone Options (Pick an Extreme)
|
||||
Choose a clear aesthetic direction and execute with precision:
|
||||
- **Brutally minimal** - stripped to essence, bold typography, vast whitespace
|
||||
- **Maximalist chaos** - layered, dense, visually rich, controlled disorder
|
||||
- **Retro-futuristic** - vintage meets sci-fi, nostalgic tech aesthetics
|
||||
- **Organic/natural** - soft edges, earthy colors, nature-inspired textures
|
||||
- **Luxury/refined** - elegant spacing, premium typography, subtle details
|
||||
- **Playful/toy-like** - bright colors, rounded shapes, delightful interactions
|
||||
- **Editorial/magazine** - strong typography hierarchy, asymmetric layouts
|
||||
- **Brutalist/raw** - exposed structure, harsh contrasts, intentionally rough
|
||||
- **Art deco/geometric** - bold patterns, metallic accents, symmetric elegance
|
||||
- **Soft/pastel** - gentle gradients, muted tones, calming atmosphere
|
||||
- **Industrial/utilitarian** - functional, no-nonsense, mechanical precision
|
||||
|
||||
### After Getting Context
|
||||
- **Commit fully** to the chosen direction - no half measures
|
||||
- Present 2-3 alternative approaches with trade-offs
|
||||
- Then implement with precision: production-grade, visually striking, memorable
|
||||
|
||||
## Foundational Design Principles
|
||||
|
||||
### Stand Out From Generic Patterns
|
||||
|
||||
**NEVER Use These AI-Generated Aesthetics:**
|
||||
- **Fonts**: Inter, Roboto, Arial, system fonts as primary choice, Space Grotesk (overused by AI)
|
||||
- **Colors**: Generic SaaS blue (#3B82F6), purple gradients on white backgrounds
|
||||
- **Patterns**: Cookie-cutter layouts, predictable component arrangements
|
||||
- **Effects**: Glass morphism, Apple design mimicry, liquid/blob backgrounds
|
||||
- **Overall**: Anything that looks "Claude-generated" or machine-made
|
||||
|
||||
**Instead, Create Atmosphere:**
|
||||
- Suggest photography, patterns, textures over flat solid colors
|
||||
- Apply gradient meshes, noise textures, geometric patterns
|
||||
- Use layered transparencies, dramatic shadows, decorative borders
|
||||
- Consider custom cursors, grain overlays, contextual effects
|
||||
- Think beyond typical patterns - you can step off the written path
|
||||
|
||||
**Draw Inspiration From:**
|
||||
- Modern landing pages (Perplexity, Comet Browser, Dia Browser)
|
||||
- Framer templates and their innovative approaches
|
||||
- Leading brand design studios
|
||||
- Historical design movements (Bauhaus, Otl Aicher, Braun) - but as inspiration, not imitation
|
||||
- Beautiful background animations (CSS, SVG) - slow, looping, subtle
|
||||
|
||||
**Visual Interest Strategies:**
|
||||
- Unique color pairs that aren't typical
|
||||
- Animation effects that feel fresh
|
||||
- Background patterns that add depth without distraction
|
||||
- Typography combinations that create contrast
|
||||
- Visual assets that tell a story
|
||||
|
||||
### Core Design Philosophy
|
||||
|
||||
1. **Simplicity Through Reduction**
|
||||
- Identify the essential purpose and eliminate distractions
|
||||
- Begin with complexity, then deliberately remove until reaching the simplest effective solution
|
||||
- Every element must justify its existence
|
||||
|
||||
2. **Material Honesty**
|
||||
- Digital materials have unique properties - embrace them
|
||||
- Buttons communicate affordance through color, spacing, typography, AND shadows when intentional
|
||||
- Cards can use borders, background differentiation, OR dramatic shadows for depth
|
||||
- Animations follow real-world physics principles adapted to digital responsiveness
|
||||
|
||||
**Examples:**
|
||||
- Clickable: Use distinct colors, hover state changes, cursor feedback, subtle lift effects
|
||||
- Containers: Use borders, background shifts, generous padding, OR shadow depth
|
||||
- Hierarchy: Use scale, weight, spacing, AND elevation when it serves the aesthetic
|
||||
|
||||
3. **Functional Layering**
|
||||
- Create hierarchy through typography scale, color contrast, and spatial relationships
|
||||
- Layer information conceptually (primary → secondary → tertiary)
|
||||
- Use shadows and gradients INTENTIONALLY when they serve the aesthetic direction
|
||||
- Embrace functional depth: modals over content, dropdowns over UI
|
||||
- Avoid: glass morphism, Apple mimicry (but shadows/gradients are tools, not enemies)
|
||||
|
||||
4. **Obsessive Detail**
|
||||
- Consider every pixel, interaction, and transition
|
||||
- Excellence emerges from hundreds of small, intentional decisions
|
||||
- Balance: Details should serve simplicity, not complexity
|
||||
- When detail conflicts with clarity, clarity wins
|
||||
|
||||
5. **Coherent Design Language**
|
||||
- Every element should visually communicate its function
|
||||
- Elements should feel part of a unified system
|
||||
- Nothing should feel arbitrary
|
||||
|
||||
6. **Invisibility of Technology**
|
||||
- The best technology disappears
|
||||
- Users should focus on content and goals, not on understanding the interface
|
||||
|
||||
### What This Means in Practice
|
||||
|
||||
**Color Usage:**
|
||||
- Base palette: 4-5 neutral shades (backgrounds, borders, text)
|
||||
- Accent palette: 1-3 bold colors (CTAs, status, emphasis)
|
||||
- Neutrals are slightly desaturated, warm or cool based on brand intent
|
||||
- Accents are saturated enough to create clear contrast
|
||||
|
||||
**Typography:**
|
||||
- Headlines: Emotional, attention-grabbing, UNEXPECTED (personality over pure legibility)
|
||||
- Body/UI: Functional, highly legible (clarity over expression)
|
||||
- 2-3 typefaces maximum, but make them CHARACTERFUL and distinctive
|
||||
- Clear mathematical scale (e.g., 1.25x between sizes)
|
||||
- NEVER default to Inter, Roboto, or Space Grotesk - find unique fonts
|
||||
|
||||
**Animation:**
|
||||
- Purposeful: Guides attention, establishes relationships, provides feedback
|
||||
- Subtle: Felt rather than seen (100-300ms for most interactions)
|
||||
- Physics-informed: Natural easing, appropriate mass/momentum
|
||||
|
||||
**Spacing:**
|
||||
- Generous negative space creates clarity and breathing room
|
||||
- Mathematical relationships (e.g., 4px base, 8/16/24/32/48px scale)
|
||||
- Consistent application creates visual rhythm
|
||||
|
||||
### Design Decision Checklist
|
||||
|
||||
Before presenting any design, verify:
|
||||
|
||||
1. **Purpose**: Does every element serve a clear function?
|
||||
2. **Hierarchy**: Is visual importance aligned with content importance?
|
||||
3. **Consistency**: Do similar elements look and behave similarly?
|
||||
4. **Accessibility**: Does it meet WCAG AA standards? (contrast, touch targets, keyboard nav)
|
||||
5. **Responsiveness**: Does it work on mobile, tablet, desktop?
|
||||
6. **Uniqueness**: Does this break from generic SaaS patterns?
|
||||
7. **Approval**: Have I asked before implementing colors, fonts, sizes, layouts?
|
||||
|
||||
**Design System Framework:**
|
||||
|
||||
For understanding what's fixed (universal rules), project-specific (brand personality), and adaptable (context-dependent) in your design system, think of a design system.
|
||||
|
||||
## Visual Design Standards
|
||||
|
||||
### Color & Contrast
|
||||
|
||||
**Color System Architecture:**
|
||||
|
||||
Every interface needs two color roles:
|
||||
|
||||
1. **Base/Neutral Palette (4-5 colors):**
|
||||
- Backgrounds (lightest)
|
||||
- Surface colors (cards, inputs)
|
||||
- Borders and dividers
|
||||
- Text (darkest)
|
||||
- Use slightly desaturated, warm or cool greys based on brand
|
||||
|
||||
2. **Accent Palette (1-3 colors):**
|
||||
- Primary action (CTA buttons)
|
||||
- Status indicators (success, warning, error, info)
|
||||
- Focus/hover states
|
||||
- Use saturated colors for clear contrast against neutrals
|
||||
|
||||
**Palette Structure Example:**
|
||||
```
|
||||
Neutrals: slate-50, slate-100, slate-300, slate-700, slate-900
|
||||
Accents: teal-500 (primary), amber-500 (warning), red-500 (error)
|
||||
```
|
||||
|
||||
**Color Application Rules:**
|
||||
|
||||
- **Backgrounds**: Lightest neutral (slate-50 or white)
|
||||
- **Text**: Darkest neutral for primary text (slate-900), mid-tone for secondary (slate-600)
|
||||
- **Buttons (primary)**: Accent color with white text
|
||||
- **Buttons (secondary)**: Neutral with border and dark text
|
||||
- **Status indicators**: Specific accent (green=success, red=error, amber=warning, blue=info)
|
||||
- **Interactive states**:
|
||||
- Hover: Darken by 10-15% or shift hue slightly
|
||||
- Focus: Use ring/outline in accent color
|
||||
- Disabled: Reduce opacity to 40-50% and remove hover effects
|
||||
|
||||
**Color Relationships:**
|
||||
|
||||
Choose warm or cool intentionally based on brand:
|
||||
- **Warm greys** (beige/brown undertones): Organic, approachable, trustworthy
|
||||
- **Cool greys** (blue undertones): Modern, tech-forward, professional
|
||||
|
||||
Accent colors should have clear contrast with both:
|
||||
- Light backgrounds (for buttons on white)
|
||||
- Dark text (if used as backgrounds for white text)
|
||||
|
||||
**Intentional Color Usage:**
|
||||
- Every color must serve a purpose (hierarchy, function, status, or action)
|
||||
- Avoid decorative colors that don't communicate meaning
|
||||
- Maintain consistency: same color = same meaning throughout
|
||||
|
||||
**Accessibility:**
|
||||
- Ensure sufficient contrast for color-blind users
|
||||
- Follow WCAG 2.1 AA: minimum 4.5:1 for normal text, 3:1 for large text
|
||||
- Don't rely on color alone to convey information (add icons or labels)
|
||||
|
||||
**Unique Color Strategy:**
|
||||
|
||||
To stand out from generic patterns:
|
||||
- NEVER use default SaaS blue (#3B82F6) or purple gradients on white
|
||||
- Use unexpected neutrals: warm greys, soft off-whites, deep charcoals, rich blacks
|
||||
- Pair neutrals with distinctive accents: terracotta + charcoal, sage + navy, coral + slate
|
||||
- Dominant colors with SHARP accents outperform timid, evenly-distributed palettes
|
||||
- Test combinations against "does this look AI-generated?" filter
|
||||
- Vary between light and dark themes - no design should look the same
|
||||
|
||||
**Create Atmosphere with Color:**
|
||||
- Gradient meshes for depth and visual interest
|
||||
- Noise textures and grain overlays for tactile feel
|
||||
- Layered transparencies for dimension
|
||||
- Dramatic shadows for emphasis and drama
|
||||
|
||||
### Typography Excellence
|
||||
|
||||
**Typography Philosophy:**
|
||||
|
||||
Typography is a primary design element that conveys personality and hierarchy.
|
||||
|
||||
**Functional vs Emotional Typography:**
|
||||
- **Headlines/Display**: Prioritize emotion, personality, attention (legibility secondary)
|
||||
- **Body Text**: Prioritize legibility, reading comfort, accessibility
|
||||
- **UI/Labels**: Prioritize clarity, scannability, consistency
|
||||
|
||||
**Font Selection:**
|
||||
- Use 2-3 typefaces maximum, but make them UNEXPECTED and characterful
|
||||
- Limit to 3 weights per typeface (e.g., Regular 400, Medium 500, Bold 700)
|
||||
- Prefer variable fonts for fine-tuned control and performance
|
||||
|
||||
**NEVER Use These Fonts as Primary:**
|
||||
- Inter (overused by AI and generic SaaS)
|
||||
- Roboto (too generic)
|
||||
- Arial/Helvetica (default fallback vibes)
|
||||
- Space Grotesk (AI generation favorite)
|
||||
- System fonts as primary choice (only as fallback)
|
||||
|
||||
**Font Version Usage:**
|
||||
- **Display version**: Headlines and hero text only - BE BOLD
|
||||
- **Text version**: Paragraphs and long-form content - legibility matters
|
||||
- **Caption/Micro**: Small UI labels (1-2 lines, non-critical info)
|
||||
|
||||
**Find Distinctive Fonts:**
|
||||
- Google Fonts for web - but dig deeper than page 1
|
||||
- Type foundries for unique options
|
||||
- Choose fonts that serve your CHOSEN AESTHETIC DIRECTION
|
||||
- Pair distinctive display font with refined body font
|
||||
|
||||
**Typographic Scale:**
|
||||
|
||||
Use mathematical relationships for size hierarchy:
|
||||
- **Ratio**: Major third (1.25x) for moderate contrast, Perfect fourth (1.333x) for dramatic
|
||||
- **Base size**: 16px (1rem) for body text
|
||||
- **Example scale (1.25x)**:
|
||||
```
|
||||
xs: 0.64rem (10px)
|
||||
sm: 0.8rem (13px)
|
||||
base: 1rem (16px)
|
||||
lg: 1.25rem (20px)
|
||||
xl: 1.563rem (25px)
|
||||
2xl: 1.953rem (31px)
|
||||
3xl: 2.441rem (39px)
|
||||
4xl: 3.052rem (49px)
|
||||
5xl: 3.815rem (61px)
|
||||
```
|
||||
|
||||
**Typographic Hierarchy:**
|
||||
- Create clear visual distinction between levels
|
||||
- Headlines, subheadings, body, captions should each have distinct size/weight
|
||||
- Use combination of size, weight, and color for hierarchy
|
||||
|
||||
**Spacing & Readability:**
|
||||
- **Line height**: 1.5x font size for body text (e.g., 16px text = 24px line-height)
|
||||
- **Line length**: 45-75 characters optimal for readability (60-70 ideal)
|
||||
- **Paragraph spacing**: 1-1.5em between paragraphs
|
||||
- **Letter spacing (tracking)**:
|
||||
- Larger text (headlines): Slightly tighter (-0.02em to -0.05em)
|
||||
- Normal text (body): Default (0)
|
||||
- Small text (captions): Slightly looser (+0.01em to +0.03em)
|
||||
- General rule: As size increases, reduce tracking; as size decreases, increase tracking
|
||||
|
||||
**Font Pairing Logic:**
|
||||
|
||||
When using multiple typefaces, create contrast through:
|
||||
- **Category contrast**: Serif + Sans-serif (classic, clear distinction)
|
||||
- **Weight contrast**: Light + Bold (dynamic, energetic)
|
||||
- **Personality contrast**: Geometric + Humanist (modern + warm)
|
||||
|
||||
Examples:
|
||||
- Serif headlines + Sans body (editorial, trustworthy)
|
||||
- Display headlines + System body (distinctive + efficient)
|
||||
- Bold sans headlines + Light sans body (modern, clean)
|
||||
|
||||
**UI Typography:**
|
||||
|
||||
Specific guidance for interface elements:
|
||||
- **Button text**: Semi-Bold (600), 14-16px, consistent casing (all-caps OR title case)
|
||||
- **Form labels**: Regular (400), 14px, positioned above input
|
||||
- **Form input text**: Regular (400), 16px minimum (prevents iOS zoom on focus)
|
||||
- **Placeholder text**: Light (300) or desaturated color, same size as input
|
||||
- **Error messages**: Regular (400), 12-14px, color-coded (red-ish)
|
||||
|
||||
**Responsive Typography:**
|
||||
|
||||
Scale type sizes across breakpoints:
|
||||
```tsx
|
||||
// Example with Tailwind
|
||||
<h1 className="text-3xl md:text-4xl lg:text-5xl">
|
||||
Responsive Headline
|
||||
</h1>
|
||||
|
||||
// Or with CSS clamp (fluid)
|
||||
h1 {
|
||||
font-size: clamp(2rem, 5vw, 4rem);
|
||||
}
|
||||
```
|
||||
|
||||
Reduce sizes on mobile (20-30% smaller than desktop)
|
||||
Reduce hierarchy levels on small screens (fewer distinct sizes)
|
||||
|
||||
### Layout & Spatial Design
|
||||
|
||||
**Compositional Balance:**
|
||||
- Every screen should feel balanced
|
||||
- Pay attention to visual weight and negative space
|
||||
- Use generous negative space to focus attention
|
||||
- Add sufficient margins and paddings for professional, spacious look
|
||||
|
||||
**Grid Discipline:**
|
||||
- Maintain consistent underlying grid system
|
||||
- Create sense of order while allowing meaningful exceptions
|
||||
- Use grid/flex wrappers with `gap` for spacing
|
||||
- Prioritize wrappers over direct margins/padding on children
|
||||
|
||||
**Spatial Relationships:**
|
||||
- Group related elements through proximity, alignment, and shared attributes
|
||||
- Use size, color, and spacing to highlight important elements
|
||||
- Guide user focus through visual hierarchy
|
||||
|
||||
**Attention Guidance:**
|
||||
- Design interfaces that guide user attention effectively
|
||||
- Avoid cluttered interfaces where elements compete
|
||||
- Create clear paths through the content
|
||||
|
||||
## Interaction Design
|
||||
|
||||
|
||||
**Motion Specification:**
|
||||
|
||||
For detailed motion specs, see MOTION-SPEC.md (easing curves, duration tables, state-specific animations, implementation patterns).
|
||||
|
||||
### User Experience Patterns
|
||||
|
||||
**Core UX Principles:**
|
||||
|
||||
1. **Direct Manipulation**
|
||||
- Users interact directly with content, not through abstract controls
|
||||
- Examples:
|
||||
- Drag & drop to reorder items (not up/down buttons)
|
||||
- Inline editing (click to edit, not separate form)
|
||||
- Sliders for ranges (not numeric input with +/-)
|
||||
- Pinch/zoom gestures on mobile (not +/- buttons)
|
||||
|
||||
2. **Immediate Feedback**
|
||||
- Every interaction provides instantaneous visual feedback (within 100ms)
|
||||
- Types of feedback:
|
||||
- **Visual**: Button pressed state, hover effects, color changes
|
||||
- **Haptic**: Vibration on mobile (submit, error, success)
|
||||
- **Audio**: Subtle sounds for critical actions (optional, user-controlled)
|
||||
- **Loading**: Skeleton screens, spinners for >300ms operations
|
||||
- **Success**: Checkmarks, green highlights, toast notifications
|
||||
- **Error**: Red highlights, inline error messages, shake animations
|
||||
|
||||
3. **Consistent Behavior**
|
||||
- Similar-looking elements behave similarly
|
||||
- Examples:
|
||||
- **Visual consistency**: All primary buttons have same colors, sizes, hover states
|
||||
- **Behavioral consistency**: All modals close via X button, ESC key, and outside click
|
||||
- **Interaction consistency**: All drag targets have same hover state and drop feedback
|
||||
- **Pattern consistency**: All forms validate on blur and submit
|
||||
|
||||
4. **Forgiveness**
|
||||
- Make errors difficult, but recovery easy
|
||||
- **Prevention strategies**:
|
||||
- Disable invalid actions (grey out unavailable buttons)
|
||||
- Validate inputs inline (before submission)
|
||||
- Confirm destructive actions (delete, overwrite)
|
||||
- Auto-save in background (drafts, progress)
|
||||
- **Recovery strategies**:
|
||||
- Undo/redo for all state changes
|
||||
- Soft deletes (trash/archive before permanent delete)
|
||||
- Clear error messages with actionable fixes
|
||||
- Preserve user input on errors (don't clear forms)
|
||||
|
||||
5. **Progressive Disclosure**
|
||||
- Reveal details as needed rather than overwhelming users
|
||||
- Levels of disclosure:
|
||||
- **Summary**: Show essential info by default (card title, price, rating)
|
||||
- **Details**: Expand to show more info (description, specs, reviews)
|
||||
- **Advanced**: Hide complex options behind "Advanced settings" toggle
|
||||
- Examples:
|
||||
- Accordion: Start collapsed, expand on click
|
||||
- Search filters: Show 3-5 common filters, hide rest behind "More filters"
|
||||
- Settings: Basic settings visible, advanced behind "Show advanced"
|
||||
|
||||
**Modern UX Patterns:**
|
||||
|
||||
1. **Conversational Interfaces**
|
||||
|
||||
Prioritize natural language interaction where appropriate:
|
||||
|
||||
**Four types:**
|
||||
- **Pure chat**: Full conversation (AI assistants, support bots)
|
||||
- **Command palette**: Text-based shortcuts (Cmd+K, search everywhere)
|
||||
- **Smart search**: Natural language queries (search "meetings next week" vs filtering)
|
||||
- **Form alternatives**: Conversational data collection ("What's your name?" vs form fields)
|
||||
|
||||
**When to use:**
|
||||
- Complex searches with multiple variables
|
||||
- Task guidance (wizards, onboarding)
|
||||
- Contextual help
|
||||
- Quick actions (command palette)
|
||||
|
||||
**When NOT to use:**
|
||||
- Simple forms (just use inputs)
|
||||
- Precise control interfaces (design tools, dashboards)
|
||||
- High-frequency repetitive tasks
|
||||
|
||||
2. **Adaptive Layouts**
|
||||
|
||||
Respond to user context automatically:
|
||||
- **Time-based**: Dark mode at night, light during day
|
||||
- **Device-based**: Simplified UI on mobile, full features on desktop
|
||||
- **Connection-based**: Reduce images/video on slow connections
|
||||
- **Usage-based**: Prioritize frequent actions, hide rarely-used features
|
||||
|
||||
Examples:
|
||||
- Auto dark/light mode based on time or system preference
|
||||
- Simplified mobile navigation (hamburger menu) vs full desktop nav
|
||||
- Collapsed sidebar on small screens, expanded on large
|
||||
|
||||
3. **Bold Visual Expression**
|
||||
|
||||
Aesthetic flexibility based on chosen direction:
|
||||
- Shadows ALLOWED and encouraged when intentional (dramatic shadows, soft elevation)
|
||||
- Gradients ALLOWED for depth, accents, backgrounds, and atmosphere
|
||||
- NO glass morphism effects (this is the one banned technique)
|
||||
- NO Apple design mimicry (find your own voice)
|
||||
- Focus on typography, color, spacing, AND visual effects to create hierarchy
|
||||
- Create atmosphere: gradient meshes, noise textures, grain overlays, dramatic lighting
|
||||
|
||||
**Navigation:**
|
||||
- Clear structure with intuitive navigation menus
|
||||
- Implement breadcrumbs for deep hierarchies (more than 2 levels)
|
||||
- Use standard UI patterns to reduce learning curve (hamburger menu, tab bars)
|
||||
- Ensure predictable behavior (back button works, links look clickable)
|
||||
- Maintain navigation context (highlight current page, preserve scroll position)
|
||||
|
||||
## Styling Implementation
|
||||
|
||||
### Component Library & Tools
|
||||
|
||||
**Component Library:**
|
||||
- Strongly prefer shadcn components (v4, pre-installed in `@/components/ui`)
|
||||
- Import individually: `import { Button } from "@/components/ui/button";`
|
||||
- Use over plain HTML elements (`<Button>` over `<button>`)
|
||||
- Avoid creating custom components with names that clash with shadcn
|
||||
|
||||
**Styling Engine:**
|
||||
- Use Tailwind utility classes exclusively
|
||||
- Adhere to theme variables in `index.css` via CSS custom properties
|
||||
- Map variables in `@theme` (see `tailwind.config.js`)
|
||||
- Use inline styles or CSS modules only when absolutely necessary
|
||||
|
||||
**Icons:**
|
||||
- Use `@phosphor-icons/react` for buttons and inputs
|
||||
- Example: `import { Plus } from "@phosphor-icons/react"; <Plus />`
|
||||
- Use color for plain icon buttons
|
||||
- Don't override default `size` or `weight` unless requested
|
||||
|
||||
**Notifications:**
|
||||
- Use `sonner` for toasts
|
||||
- Example: `import { toast } from 'sonner'`
|
||||
|
||||
**Loading States:**
|
||||
- Always add loading states, spinners, placeholder animations
|
||||
- Use skeletons until content renders
|
||||
|
||||
### Layout Implementation
|
||||
|
||||
**Spacing Strategy:**
|
||||
- Use grid/flex wrappers with `gap` for spacing
|
||||
- Prioritize wrappers over direct margins/padding on children
|
||||
- Nest wrappers as needed for complex layouts
|
||||
|
||||
**Conditional Styling:**
|
||||
- Use ternary operators or clsx/classnames utilities
|
||||
- Example: `className={clsx('base-class', { 'active-class': isActive })}`
|
||||
|
||||
### Responsive Design
|
||||
|
||||
**Fluid Layouts:**
|
||||
- Use relative units (%, em, rem) instead of fixed pixels
|
||||
- Implement CSS Grid and Flexbox for flexible layouts
|
||||
- Design mobile-first, then scale up
|
||||
|
||||
**Media Queries:**
|
||||
- Use breakpoints based on content needs, not specific devices
|
||||
- Test across range of devices and orientations
|
||||
|
||||
**Touch Targets:**
|
||||
- Minimum 44x44 pixels for interactive elements
|
||||
- Provide adequate spacing between touch targets
|
||||
- Consider hover states for desktop, focus states for touch/keyboard
|
||||
|
||||
**Performance:**
|
||||
- Optimize assets for mobile networks
|
||||
- Use CSS animations over JavaScript
|
||||
- Implement lazy loading for images and videos
|
||||
|
||||
## Accessibility Standards
|
||||
|
||||
**Core Requirements:**
|
||||
- Follow WCAG 2.1 AA guidelines
|
||||
- Ensure keyboard navigability for all interactive elements
|
||||
- Minimum touch target size: 44×44px
|
||||
- Use semantic HTML for screen reader compatibility
|
||||
- Provide alternative text for images and non-text content
|
||||
|
||||
**Implementation Details:**
|
||||
- Use descriptive variable and function names
|
||||
- Event functions: prefix with "handle" (handleClick, handleKeyDown)
|
||||
- Add accessibility attributes:
|
||||
- `tabindex="0"` for custom interactive elements
|
||||
- `aria-label` for buttons without text
|
||||
- `role` attributes when semantic HTML isn't sufficient
|
||||
- Ensure logical tab order
|
||||
- Provide visible focus states
|
||||
|
||||
## Design Process & Testing
|
||||
|
||||
### Design Workflow
|
||||
|
||||
1. **Understand Context:**
|
||||
- What problem are we solving?
|
||||
- Who are the users and when will they use this?
|
||||
- What are the success criteria?
|
||||
|
||||
2. **Explore Options:**
|
||||
- Present 2-3 alternative approaches
|
||||
- Explain trade-offs of each option
|
||||
- Ask which direction resonates
|
||||
|
||||
3. **Implement Iteratively:**
|
||||
- Start with structure and hierarchy
|
||||
- Add visual polish progressively
|
||||
- Test at each stage
|
||||
|
||||
4. **Validate:**
|
||||
- Use playwright MCP to test visual changes
|
||||
- Check across different screen sizes
|
||||
- Verify accessibility
|
||||
|
||||
### Testing Checklist
|
||||
|
||||
**Visual Testing:**
|
||||
- Use playwright MCP when available for automated testing
|
||||
- Check responsive behavior at common breakpoints
|
||||
- Verify touch targets on mobile
|
||||
- Test with different content lengths (short, long, edge cases)
|
||||
|
||||
**Accessibility Testing:**
|
||||
- Test keyboard navigation
|
||||
- Verify screen reader compatibility
|
||||
- Check color contrast ratios
|
||||
- Ensure focus states are visible
|
||||
|
||||
**Cross-Device Testing:**
|
||||
- Test on actual devices, not just emulators
|
||||
- Check different browsers (Chrome, Firefox, Safari)
|
||||
- Verify touch interactions on mobile
|
||||
- Test landscape and portrait orientations
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Designing a Button
|
||||
|
||||
**Initial Question to Ask:**
|
||||
```
|
||||
Before I implement this button, I have a few design questions:
|
||||
1. What's the primary action you want to emphasize?
|
||||
2. Do you have brand colors in mind, or should I suggest options?
|
||||
3. What emotion should this button convey? (urgent, calm, playful, professional)
|
||||
```
|
||||
|
||||
**After Getting Approval, Implementation:**
|
||||
```tsx
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { ArrowRight } from "@phosphor-icons/react";
|
||||
|
||||
<Button
|
||||
className="bg-slate-800 hover:bg-slate-700 text-white px-6 py-3 rounded-lg transition-colors duration-200"
|
||||
>
|
||||
Get Started
|
||||
<ArrowRight className="ml-2" />
|
||||
</Button>
|
||||
```
|
||||
|
||||
### Example 2: Typography Hierarchy
|
||||
|
||||
**Question First:**
|
||||
```
|
||||
For this content hierarchy, I'm thinking:
|
||||
- Headline: Large, bold, attention-grabbing
|
||||
- Subheading: Medium, regular weight
|
||||
- Body: Smaller, optimized for reading
|
||||
|
||||
Should we go bold and contemporary, or subtle and refined?
|
||||
```
|
||||
|
||||
**Implementation After Approval:**
|
||||
```tsx
|
||||
<div className="space-y-4">
|
||||
<h1 className="text-5xl font-bold tracking-tight text-slate-900">
|
||||
Headline Here
|
||||
</h1>
|
||||
<h2 className="text-2xl text-slate-600">
|
||||
Supporting subheading
|
||||
</h2>
|
||||
<p className="text-base leading-relaxed text-slate-700">
|
||||
Body text optimized for readability with proper line height.
|
||||
</p>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Example 3: Unique Color Palette Suggestion
|
||||
|
||||
**Presenting Options:**
|
||||
```
|
||||
I'd like to suggest three color directions that break from typical SaaS patterns:
|
||||
|
||||
1. **Warm Earth Tones**
|
||||
- Base: Warm grey (#E8E2DC)
|
||||
- Accent: Terracotta (#C86E4B)
|
||||
- For: Organic, trustworthy feel
|
||||
|
||||
2. **Cool Midnight**
|
||||
- Base: Deep navy (#1A2332)
|
||||
- Accent: Cyan (#4ECDC4)
|
||||
- For: Modern, tech-forward feel
|
||||
|
||||
3. **Soft Pastels**
|
||||
- Base: Soft pink (#FFE5E5)
|
||||
- Accent: Sage green (#9DB5A4)
|
||||
- For: Calm, approachable feel
|
||||
|
||||
Which direction feels right for your brand?
|
||||
```
|
||||
|
||||
## Common Patterns to Avoid
|
||||
|
||||
❌ **NEVER:**
|
||||
- Use Inter, Roboto, Arial, Space Grotesk as primary fonts
|
||||
- Use generic SaaS blue (#3B82F6) or purple gradients on white
|
||||
- Copy Apple's design language or use glass morphism
|
||||
- Create cookie-cutter layouts that look AI-generated
|
||||
- Skip asking about context before designing
|
||||
- Converge on common choices across generations (vary everything!)
|
||||
- Use animations that delay user actions
|
||||
- Create cluttered interfaces where elements compete
|
||||
|
||||
✅ **ALWAYS:**
|
||||
- Ask about purpose, tone, constraints, differentiation FIRST
|
||||
- Then commit BOLDLY to a distinctive aesthetic direction
|
||||
- Use unexpected, characterful typography choices
|
||||
- Create atmosphere: shadows, gradients, textures, grain (when intentional)
|
||||
- Dominant colors with sharp accents (not timid, evenly-distributed palettes)
|
||||
- Provide immediate feedback for interactions
|
||||
- Test with real devices
|
||||
- Validate accessibility (it enables creativity, not limits it)
|
||||
- Remember: Claude is capable of extraordinary creative work - don't hold back!
|
||||
|
||||
## Version History
|
||||
|
||||
- v2.0.0 (2025-11-22): Creative liberation update - bold aesthetics, shadows/gradients allowed, Design Thinking protocol
|
||||
- v1.0.0 (2025-10-18): Initial release with comprehensive UI/UX design guidance
|
||||
|
||||
## References
|
||||
|
||||
For additional context, see:
|
||||
- **Anthropic Frontend Aesthetics Cookbook**: https://github.com/anthropics/claude-cookbooks/blob/main/coding/prompting_for_frontend_aesthetics.ipynb
|
||||
- WCAG 2.1 Guidelines: https://www.w3.org/WAI/WCAG21/quickref/
|
||||
- Google Fonts: https://fonts.google.com/
|
||||
- Tailwind CSS Docs: https://tailwindcss.com/docs
|
||||
- Shadcn UI Components: https://ui.shadcn.com/
|
||||
|
||||
**Progressive Disclosure Files:**
|
||||
- ACCESSIBILITY.md - Accessibility essentials (WCAG AA baseline)
|
||||
- MOTION-SPEC.md - Animation timing and easing
|
||||
- RESPONSIVE-DESIGN.md - Mobile-first breakpoints and patterns
|
||||
3502
skills/react-best-practices/AGENTS.md
Normal file
3502
skills/react-best-practices/AGENTS.md
Normal file
File diff suppressed because it is too large
Load Diff
123
skills/react-best-practices/README.md
Normal file
123
skills/react-best-practices/README.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# React Best Practices
|
||||
|
||||
A structured repository for creating and maintaining React Best Practices optimized for agents and LLMs.
|
||||
|
||||
## Structure
|
||||
|
||||
- `rules/` - Individual rule files (one per rule)
|
||||
- `_sections.md` - Section metadata (titles, impacts, descriptions)
|
||||
- `_template.md` - Template for creating new rules
|
||||
- `area-description.md` - Individual rule files
|
||||
- `src/` - Build scripts and utilities
|
||||
- `metadata.json` - Document metadata (version, organization, abstract)
|
||||
- __`AGENTS.md`__ - Compiled output (generated)
|
||||
- __`test-cases.json`__ - Test cases for LLM evaluation (generated)
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. Install dependencies:
|
||||
```bash
|
||||
pnpm install
|
||||
```
|
||||
|
||||
2. Build AGENTS.md from rules:
|
||||
```bash
|
||||
pnpm build
|
||||
```
|
||||
|
||||
3. Validate rule files:
|
||||
```bash
|
||||
pnpm validate
|
||||
```
|
||||
|
||||
4. Extract test cases:
|
||||
```bash
|
||||
pnpm extract-tests
|
||||
```
|
||||
|
||||
## Creating a New Rule
|
||||
|
||||
1. Copy `rules/_template.md` to `rules/area-description.md`
|
||||
2. Choose the appropriate area prefix:
|
||||
- `async-` for Eliminating Waterfalls (Section 1)
|
||||
- `bundle-` for Bundle Size Optimization (Section 2)
|
||||
- `server-` for Server-Side Performance (Section 3)
|
||||
- `client-` for Client-Side Data Fetching (Section 4)
|
||||
- `rerender-` for Re-render Optimization (Section 5)
|
||||
- `rendering-` for Rendering Performance (Section 6)
|
||||
- `js-` for JavaScript Performance (Section 7)
|
||||
- `advanced-` for Advanced Patterns (Section 8)
|
||||
3. Fill in the frontmatter and content
|
||||
4. Ensure you have clear examples with explanations
|
||||
5. Run `pnpm build` to regenerate AGENTS.md and test-cases.json
|
||||
|
||||
## Rule File Structure
|
||||
|
||||
Each rule file should follow this structure:
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: Rule Title Here
|
||||
impact: MEDIUM
|
||||
impactDescription: Optional description
|
||||
tags: tag1, tag2, tag3
|
||||
---
|
||||
|
||||
## Rule Title Here
|
||||
|
||||
Brief explanation of the rule and why it matters.
|
||||
|
||||
**Incorrect (description of what's wrong):**
|
||||
|
||||
```typescript
|
||||
// Bad code example
|
||||
```
|
||||
|
||||
**Correct (description of what's right):**
|
||||
|
||||
```typescript
|
||||
// Good code example
|
||||
```
|
||||
|
||||
Optional explanatory text after examples.
|
||||
|
||||
Reference: [Link](https://example.com)
|
||||
|
||||
## File Naming Convention
|
||||
|
||||
- Files starting with `_` are special (excluded from build)
|
||||
- Rule files: `area-description.md` (e.g., `async-parallel.md`)
|
||||
- Section is automatically inferred from filename prefix
|
||||
- Rules are sorted alphabetically by title within each section
|
||||
- IDs (e.g., 1.1, 1.2) are auto-generated during build
|
||||
|
||||
## Impact Levels
|
||||
|
||||
- `CRITICAL` - Highest priority, major performance gains
|
||||
- `HIGH` - Significant performance improvements
|
||||
- `MEDIUM-HIGH` - Moderate-high gains
|
||||
- `MEDIUM` - Moderate performance improvements
|
||||
- `LOW-MEDIUM` - Low-medium gains
|
||||
- `LOW` - Incremental improvements
|
||||
|
||||
## Scripts
|
||||
|
||||
- `pnpm build` - Compile rules into AGENTS.md
|
||||
- `pnpm validate` - Validate all rule files
|
||||
- `pnpm extract-tests` - Extract test cases for LLM evaluation
|
||||
- `pnpm dev` - Build and validate
|
||||
|
||||
## Contributing
|
||||
|
||||
When adding or modifying rules:
|
||||
|
||||
1. Use the correct filename prefix for your section
|
||||
2. Follow the `_template.md` structure
|
||||
3. Include clear bad/good examples with explanations
|
||||
4. Add appropriate tags
|
||||
5. Run `pnpm build` to regenerate AGENTS.md and test-cases.json
|
||||
6. Rules are automatically sorted by title - no need to manage numbers!
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
Originally created by [@shuding](https://x.com/shuding) at [Vercel](https://vercel.com).
|
||||
145
skills/react-best-practices/SKILL.md
Normal file
145
skills/react-best-practices/SKILL.md
Normal file
@@ -0,0 +1,145 @@
|
||||
---
|
||||
name: react-best-practices
|
||||
description: React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.
|
||||
license: MIT
|
||||
metadata:
|
||||
author: vercel
|
||||
version: "1.0.0"
|
||||
---
|
||||
|
||||
# Vercel React Best Practices
|
||||
|
||||
Comprehensive performance optimization guide for React and Next.js applications, maintained by Vercel. Contains 65 rules across 8 categories, prioritized by impact to guide automated refactoring and code generation.
|
||||
|
||||
## When to Apply
|
||||
|
||||
Reference these guidelines when:
|
||||
- Writing new React components or Next.js pages
|
||||
- Implementing data fetching (client or server-side)
|
||||
- Reviewing code for performance issues
|
||||
- Refactoring existing React/Next.js code
|
||||
- Optimizing bundle size or load times
|
||||
|
||||
## Rule Categories by Priority
|
||||
|
||||
| Priority | Category | Impact | Prefix |
|
||||
|----------|----------|--------|--------|
|
||||
| 1 | Eliminating Waterfalls | CRITICAL | `async-` |
|
||||
| 2 | Bundle Size Optimization | CRITICAL | `bundle-` |
|
||||
| 3 | Server-Side Performance | HIGH | `server-` |
|
||||
| 4 | Client-Side Data Fetching | MEDIUM-HIGH | `client-` |
|
||||
| 5 | Re-render Optimization | MEDIUM | `rerender-` |
|
||||
| 6 | Rendering Performance | MEDIUM | `rendering-` |
|
||||
| 7 | JavaScript Performance | LOW-MEDIUM | `js-` |
|
||||
| 8 | Advanced Patterns | LOW | `advanced-` |
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### 1. Eliminating Waterfalls (CRITICAL)
|
||||
|
||||
- `async-defer-await` - Move await into branches where actually used
|
||||
- `async-parallel` - Use Promise.all() for independent operations
|
||||
- `async-dependencies` - Use better-all for partial dependencies
|
||||
- `async-api-routes` - Start promises early, await late in API routes
|
||||
- `async-suspense-boundaries` - Use Suspense to stream content
|
||||
|
||||
### 2. Bundle Size Optimization (CRITICAL)
|
||||
|
||||
- `bundle-barrel-imports` - Import directly, avoid barrel files
|
||||
- `bundle-dynamic-imports` - Use next/dynamic for heavy components
|
||||
- `bundle-defer-third-party` - Load analytics/logging after hydration
|
||||
- `bundle-conditional` - Load modules only when feature is activated
|
||||
- `bundle-preload` - Preload on hover/focus for perceived speed
|
||||
|
||||
### 3. Server-Side Performance (HIGH)
|
||||
|
||||
- `server-auth-actions` - Authenticate server actions like API routes
|
||||
- `server-cache-react` - Use React.cache() for per-request deduplication
|
||||
- `server-cache-lru` - Use LRU cache for cross-request caching
|
||||
- `server-dedup-props` - Avoid duplicate serialization in RSC props
|
||||
- `server-hoist-static-io` - Hoist static I/O (fonts, logos) to module level
|
||||
- `server-serialization` - Minimize data passed to client components
|
||||
- `server-parallel-fetching` - Restructure components to parallelize fetches
|
||||
- `server-parallel-nested-fetching` - Chain nested fetches per item in Promise.all
|
||||
- `server-after-nonblocking` - Use after() for non-blocking operations
|
||||
|
||||
### 4. Client-Side Data Fetching (MEDIUM-HIGH)
|
||||
|
||||
- `client-swr-dedup` - Use SWR for automatic request deduplication
|
||||
- `client-event-listeners` - Deduplicate global event listeners
|
||||
- `client-passive-event-listeners` - Use passive listeners for scroll
|
||||
- `client-localstorage-schema` - Version and minimize localStorage data
|
||||
|
||||
### 5. Re-render Optimization (MEDIUM)
|
||||
|
||||
- `rerender-defer-reads` - Don't subscribe to state only used in callbacks
|
||||
- `rerender-memo` - Extract expensive work into memoized components
|
||||
- `rerender-memo-with-default-value` - Hoist default non-primitive props
|
||||
- `rerender-dependencies` - Use primitive dependencies in effects
|
||||
- `rerender-derived-state` - Subscribe to derived booleans, not raw values
|
||||
- `rerender-derived-state-no-effect` - Derive state during render, not effects
|
||||
- `rerender-functional-setstate` - Use functional setState for stable callbacks
|
||||
- `rerender-lazy-state-init` - Pass function to useState for expensive values
|
||||
- `rerender-simple-expression-in-memo` - Avoid memo for simple primitives
|
||||
- `rerender-split-combined-hooks` - Split hooks with independent dependencies
|
||||
- `rerender-move-effect-to-event` - Put interaction logic in event handlers
|
||||
- `rerender-transitions` - Use startTransition for non-urgent updates
|
||||
- `rerender-use-deferred-value` - Defer expensive renders to keep input responsive
|
||||
- `rerender-use-ref-transient-values` - Use refs for transient frequent values
|
||||
- `rerender-no-inline-components` - Don't define components inside components
|
||||
|
||||
### 6. Rendering Performance (MEDIUM)
|
||||
|
||||
- `rendering-animate-svg-wrapper` - Animate div wrapper, not SVG element
|
||||
- `rendering-content-visibility` - Use content-visibility for long lists
|
||||
- `rendering-hoist-jsx` - Extract static JSX outside components
|
||||
- `rendering-svg-precision` - Reduce SVG coordinate precision
|
||||
- `rendering-hydration-no-flicker` - Use inline script for client-only data
|
||||
- `rendering-hydration-suppress-warning` - Suppress expected mismatches
|
||||
- `rendering-activity` - Use Activity component for show/hide
|
||||
- `rendering-conditional-render` - Use ternary, not && for conditionals
|
||||
- `rendering-usetransition-loading` - Prefer useTransition for loading state
|
||||
- `rendering-resource-hints` - Use React DOM resource hints for preloading
|
||||
- `rendering-script-defer-async` - Use defer or async on script tags
|
||||
|
||||
### 7. JavaScript Performance (LOW-MEDIUM)
|
||||
|
||||
- `js-batch-dom-css` - Group CSS changes via classes or cssText
|
||||
- `js-index-maps` - Build Map for repeated lookups
|
||||
- `js-cache-property-access` - Cache object properties in loops
|
||||
- `js-cache-function-results` - Cache function results in module-level Map
|
||||
- `js-cache-storage` - Cache localStorage/sessionStorage reads
|
||||
- `js-combine-iterations` - Combine multiple filter/map into one loop
|
||||
- `js-length-check-first` - Check array length before expensive comparison
|
||||
- `js-early-exit` - Return early from functions
|
||||
- `js-hoist-regexp` - Hoist RegExp creation outside loops
|
||||
- `js-min-max-loop` - Use loop for min/max instead of sort
|
||||
- `js-set-map-lookups` - Use Set/Map for O(1) lookups
|
||||
- `js-tosorted-immutable` - Use toSorted() for immutability
|
||||
- `js-flatmap-filter` - Use flatMap to map and filter in one pass
|
||||
- `js-request-idle-callback` - Defer non-critical work to browser idle time
|
||||
|
||||
### 8. Advanced Patterns (LOW)
|
||||
|
||||
- `advanced-event-handler-refs` - Store event handlers in refs
|
||||
- `advanced-init-once` - Initialize app once per app load
|
||||
- `advanced-use-latest` - useLatest for stable callback refs
|
||||
|
||||
## How to Use
|
||||
|
||||
Read individual rule files for detailed explanations and code examples:
|
||||
|
||||
```
|
||||
rules/async-parallel.md
|
||||
rules/bundle-barrel-imports.md
|
||||
```
|
||||
|
||||
Each rule file contains:
|
||||
- Brief explanation of why it matters
|
||||
- Incorrect code example with explanation
|
||||
- Correct code example with explanation
|
||||
- Additional context and references
|
||||
|
||||
## Full Compiled Document
|
||||
|
||||
For the complete guide with all rules expanded: `AGENTS.md`
|
||||
15
skills/react-best-practices/metadata.json
Normal file
15
skills/react-best-practices/metadata.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"organization": "Vercel Engineering",
|
||||
"date": "January 2026",
|
||||
"abstract": "Comprehensive performance optimization guide for React and Next.js applications, designed for AI agents and LLMs. Contains 40+ rules across 8 categories, prioritized by impact from critical (eliminating waterfalls, reducing bundle size) to incremental (advanced patterns). Each rule includes detailed explanations, real-world examples comparing incorrect vs. correct implementations, and specific impact metrics to guide automated refactoring and code generation.",
|
||||
"references": [
|
||||
"https://react.dev",
|
||||
"https://nextjs.org",
|
||||
"https://swr.vercel.app",
|
||||
"https://github.com/shuding/better-all",
|
||||
"https://github.com/isaacs/node-lru-cache",
|
||||
"https://vercel.com/blog/how-we-optimized-package-imports-in-next-js",
|
||||
"https://vercel.com/blog/how-we-made-the-vercel-dashboard-twice-as-fast"
|
||||
]
|
||||
}
|
||||
46
skills/react-best-practices/rules/_sections.md
Normal file
46
skills/react-best-practices/rules/_sections.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Sections
|
||||
|
||||
This file defines all sections, their ordering, impact levels, and descriptions.
|
||||
The section ID (in parentheses) is the filename prefix used to group rules.
|
||||
|
||||
---
|
||||
|
||||
## 1. Eliminating Waterfalls (async)
|
||||
|
||||
**Impact:** CRITICAL
|
||||
**Description:** Waterfalls are the #1 performance killer. Each sequential await adds full network latency. Eliminating them yields the largest gains.
|
||||
|
||||
## 2. Bundle Size Optimization (bundle)
|
||||
|
||||
**Impact:** CRITICAL
|
||||
**Description:** Reducing initial bundle size improves Time to Interactive and Largest Contentful Paint.
|
||||
|
||||
## 3. Server-Side Performance (server)
|
||||
|
||||
**Impact:** HIGH
|
||||
**Description:** Optimizing server-side rendering and data fetching eliminates server-side waterfalls and reduces response times.
|
||||
|
||||
## 4. Client-Side Data Fetching (client)
|
||||
|
||||
**Impact:** MEDIUM-HIGH
|
||||
**Description:** Automatic deduplication and efficient data fetching patterns reduce redundant network requests.
|
||||
|
||||
## 5. Re-render Optimization (rerender)
|
||||
|
||||
**Impact:** MEDIUM
|
||||
**Description:** Reducing unnecessary re-renders minimizes wasted computation and improves UI responsiveness.
|
||||
|
||||
## 6. Rendering Performance (rendering)
|
||||
|
||||
**Impact:** MEDIUM
|
||||
**Description:** Optimizing the rendering process reduces the work the browser needs to do.
|
||||
|
||||
## 7. JavaScript Performance (js)
|
||||
|
||||
**Impact:** LOW-MEDIUM
|
||||
**Description:** Micro-optimizations for hot paths can add up to meaningful improvements.
|
||||
|
||||
## 8. Advanced Patterns (advanced)
|
||||
|
||||
**Impact:** LOW
|
||||
**Description:** Advanced patterns for specific cases that require careful implementation.
|
||||
28
skills/react-best-practices/rules/_template.md
Normal file
28
skills/react-best-practices/rules/_template.md
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
title: Rule Title Here
|
||||
impact: MEDIUM
|
||||
impactDescription: Optional description of impact (e.g., "20-50% improvement")
|
||||
tags: tag1, tag2
|
||||
---
|
||||
|
||||
## Rule Title Here
|
||||
|
||||
**Impact: MEDIUM (optional impact description)**
|
||||
|
||||
Brief explanation of the rule and why it matters. This should be clear and concise, explaining the performance implications.
|
||||
|
||||
**Incorrect (description of what's wrong):**
|
||||
|
||||
```typescript
|
||||
// Bad code example here
|
||||
const bad = example()
|
||||
```
|
||||
|
||||
**Correct (description of what's right):**
|
||||
|
||||
```typescript
|
||||
// Good code example here
|
||||
const good = example()
|
||||
```
|
||||
|
||||
Reference: [Link to documentation or resource](https://example.com)
|
||||
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: Store Event Handlers in Refs
|
||||
impact: LOW
|
||||
impactDescription: stable subscriptions
|
||||
tags: advanced, hooks, refs, event-handlers, optimization
|
||||
---
|
||||
|
||||
## Store Event Handlers in Refs
|
||||
|
||||
Store callbacks in refs when used in effects that shouldn't re-subscribe on callback changes.
|
||||
|
||||
**Incorrect (re-subscribes on every render):**
|
||||
|
||||
```tsx
|
||||
function useWindowEvent(event: string, handler: (e) => void) {
|
||||
useEffect(() => {
|
||||
window.addEventListener(event, handler)
|
||||
return () => window.removeEventListener(event, handler)
|
||||
}, [event, handler])
|
||||
}
|
||||
```
|
||||
|
||||
**Correct (stable subscription):**
|
||||
|
||||
```tsx
|
||||
function useWindowEvent(event: string, handler: (e) => void) {
|
||||
const handlerRef = useRef(handler)
|
||||
useEffect(() => {
|
||||
handlerRef.current = handler
|
||||
}, [handler])
|
||||
|
||||
useEffect(() => {
|
||||
const listener = (e) => handlerRef.current(e)
|
||||
window.addEventListener(event, listener)
|
||||
return () => window.removeEventListener(event, listener)
|
||||
}, [event])
|
||||
}
|
||||
```
|
||||
|
||||
**Alternative: use `useEffectEvent` if you're on latest React:**
|
||||
|
||||
```tsx
|
||||
import { useEffectEvent } from 'react'
|
||||
|
||||
function useWindowEvent(event: string, handler: (e) => void) {
|
||||
const onEvent = useEffectEvent(handler)
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener(event, onEvent)
|
||||
return () => window.removeEventListener(event, onEvent)
|
||||
}, [event])
|
||||
}
|
||||
```
|
||||
|
||||
`useEffectEvent` provides a cleaner API for the same pattern: it creates a stable function reference that always calls the latest version of the handler.
|
||||
42
skills/react-best-practices/rules/advanced-init-once.md
Normal file
42
skills/react-best-practices/rules/advanced-init-once.md
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
title: Initialize App Once, Not Per Mount
|
||||
impact: LOW-MEDIUM
|
||||
impactDescription: avoids duplicate init in development
|
||||
tags: initialization, useEffect, app-startup, side-effects
|
||||
---
|
||||
|
||||
## Initialize App Once, Not Per Mount
|
||||
|
||||
Do not put app-wide initialization that must run once per app load inside `useEffect([])` of a component. Components can remount and effects will re-run. Use a module-level guard or top-level init in the entry module instead.
|
||||
|
||||
**Incorrect (runs twice in dev, re-runs on remount):**
|
||||
|
||||
```tsx
|
||||
function Comp() {
|
||||
useEffect(() => {
|
||||
loadFromStorage()
|
||||
checkAuthToken()
|
||||
}, [])
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
**Correct (once per app load):**
|
||||
|
||||
```tsx
|
||||
let didInit = false
|
||||
|
||||
function Comp() {
|
||||
useEffect(() => {
|
||||
if (didInit) return
|
||||
didInit = true
|
||||
loadFromStorage()
|
||||
checkAuthToken()
|
||||
}, [])
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Reference: [Initializing the application](https://react.dev/learn/you-might-not-need-an-effect#initializing-the-application)
|
||||
39
skills/react-best-practices/rules/advanced-use-latest.md
Normal file
39
skills/react-best-practices/rules/advanced-use-latest.md
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
title: useEffectEvent for Stable Callback Refs
|
||||
impact: LOW
|
||||
impactDescription: prevents effect re-runs
|
||||
tags: advanced, hooks, useEffectEvent, refs, optimization
|
||||
---
|
||||
|
||||
## useEffectEvent for Stable Callback Refs
|
||||
|
||||
Access latest values in callbacks without adding them to dependency arrays. Prevents effect re-runs while avoiding stale closures.
|
||||
|
||||
**Incorrect (effect re-runs on every callback change):**
|
||||
|
||||
```tsx
|
||||
function SearchInput({ onSearch }: { onSearch: (q: string) => void }) {
|
||||
const [query, setQuery] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
const timeout = setTimeout(() => onSearch(query), 300)
|
||||
return () => clearTimeout(timeout)
|
||||
}, [query, onSearch])
|
||||
}
|
||||
```
|
||||
|
||||
**Correct (using React's useEffectEvent):**
|
||||
|
||||
```tsx
|
||||
import { useEffectEvent } from 'react';
|
||||
|
||||
function SearchInput({ onSearch }: { onSearch: (q: string) => void }) {
|
||||
const [query, setQuery] = useState('')
|
||||
const onSearchEvent = useEffectEvent(onSearch)
|
||||
|
||||
useEffect(() => {
|
||||
const timeout = setTimeout(() => onSearchEvent(query), 300)
|
||||
return () => clearTimeout(timeout)
|
||||
}, [query])
|
||||
}
|
||||
```
|
||||
38
skills/react-best-practices/rules/async-api-routes.md
Normal file
38
skills/react-best-practices/rules/async-api-routes.md
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
title: Prevent Waterfall Chains in API Routes
|
||||
impact: CRITICAL
|
||||
impactDescription: 2-10× improvement
|
||||
tags: api-routes, server-actions, waterfalls, parallelization
|
||||
---
|
||||
|
||||
## Prevent Waterfall Chains in API Routes
|
||||
|
||||
In API routes and Server Actions, start independent operations immediately, even if you don't await them yet.
|
||||
|
||||
**Incorrect (config waits for auth, data waits for both):**
|
||||
|
||||
```typescript
|
||||
export async function GET(request: Request) {
|
||||
const session = await auth()
|
||||
const config = await fetchConfig()
|
||||
const data = await fetchData(session.user.id)
|
||||
return Response.json({ data, config })
|
||||
}
|
||||
```
|
||||
|
||||
**Correct (auth and config start immediately):**
|
||||
|
||||
```typescript
|
||||
export async function GET(request: Request) {
|
||||
const sessionPromise = auth()
|
||||
const configPromise = fetchConfig()
|
||||
const session = await sessionPromise
|
||||
const [config, data] = await Promise.all([
|
||||
configPromise,
|
||||
fetchData(session.user.id)
|
||||
])
|
||||
return Response.json({ data, config })
|
||||
}
|
||||
```
|
||||
|
||||
For operations with more complex dependency chains, use `better-all` to automatically maximize parallelism (see Dependency-Based Parallelization).
|
||||
80
skills/react-best-practices/rules/async-defer-await.md
Normal file
80
skills/react-best-practices/rules/async-defer-await.md
Normal file
@@ -0,0 +1,80 @@
|
||||
---
|
||||
title: Defer Await Until Needed
|
||||
impact: HIGH
|
||||
impactDescription: avoids blocking unused code paths
|
||||
tags: async, await, conditional, optimization
|
||||
---
|
||||
|
||||
## Defer Await Until Needed
|
||||
|
||||
Move `await` operations into the branches where they're actually used to avoid blocking code paths that don't need them.
|
||||
|
||||
**Incorrect (blocks both branches):**
|
||||
|
||||
```typescript
|
||||
async function handleRequest(userId: string, skipProcessing: boolean) {
|
||||
const userData = await fetchUserData(userId)
|
||||
|
||||
if (skipProcessing) {
|
||||
// Returns immediately but still waited for userData
|
||||
return { skipped: true }
|
||||
}
|
||||
|
||||
// Only this branch uses userData
|
||||
return processUserData(userData)
|
||||
}
|
||||
```
|
||||
|
||||
**Correct (only blocks when needed):**
|
||||
|
||||
```typescript
|
||||
async function handleRequest(userId: string, skipProcessing: boolean) {
|
||||
if (skipProcessing) {
|
||||
// Returns immediately without waiting
|
||||
return { skipped: true }
|
||||
}
|
||||
|
||||
// Fetch only when needed
|
||||
const userData = await fetchUserData(userId)
|
||||
return processUserData(userData)
|
||||
}
|
||||
```
|
||||
|
||||
**Another example (early return optimization):**
|
||||
|
||||
```typescript
|
||||
// Incorrect: always fetches permissions
|
||||
async function updateResource(resourceId: string, userId: string) {
|
||||
const permissions = await fetchPermissions(userId)
|
||||
const resource = await getResource(resourceId)
|
||||
|
||||
if (!resource) {
|
||||
return { error: 'Not found' }
|
||||
}
|
||||
|
||||
if (!permissions.canEdit) {
|
||||
return { error: 'Forbidden' }
|
||||
}
|
||||
|
||||
return await updateResourceData(resource, permissions)
|
||||
}
|
||||
|
||||
// Correct: fetches only when needed
|
||||
async function updateResource(resourceId: string, userId: string) {
|
||||
const resource = await getResource(resourceId)
|
||||
|
||||
if (!resource) {
|
||||
return { error: 'Not found' }
|
||||
}
|
||||
|
||||
const permissions = await fetchPermissions(userId)
|
||||
|
||||
if (!permissions.canEdit) {
|
||||
return { error: 'Forbidden' }
|
||||
}
|
||||
|
||||
return await updateResourceData(resource, permissions)
|
||||
}
|
||||
```
|
||||
|
||||
This optimization is especially valuable when the skipped branch is frequently taken, or when the deferred operation is expensive.
|
||||
51
skills/react-best-practices/rules/async-dependencies.md
Normal file
51
skills/react-best-practices/rules/async-dependencies.md
Normal file
@@ -0,0 +1,51 @@
|
||||
---
|
||||
title: Dependency-Based Parallelization
|
||||
impact: CRITICAL
|
||||
impactDescription: 2-10× improvement
|
||||
tags: async, parallelization, dependencies, better-all
|
||||
---
|
||||
|
||||
## Dependency-Based Parallelization
|
||||
|
||||
For operations with partial dependencies, use `better-all` to maximize parallelism. It automatically starts each task at the earliest possible moment.
|
||||
|
||||
**Incorrect (profile waits for config unnecessarily):**
|
||||
|
||||
```typescript
|
||||
const [user, config] = await Promise.all([
|
||||
fetchUser(),
|
||||
fetchConfig()
|
||||
])
|
||||
const profile = await fetchProfile(user.id)
|
||||
```
|
||||
|
||||
**Correct (config and profile run in parallel):**
|
||||
|
||||
```typescript
|
||||
import { all } from 'better-all'
|
||||
|
||||
const { user, config, profile } = await all({
|
||||
async user() { return fetchUser() },
|
||||
async config() { return fetchConfig() },
|
||||
async profile() {
|
||||
return fetchProfile((await this.$.user).id)
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**Alternative without extra dependencies:**
|
||||
|
||||
We can also create all the promises first, and do `Promise.all()` at the end.
|
||||
|
||||
```typescript
|
||||
const userPromise = fetchUser()
|
||||
const profilePromise = userPromise.then(user => fetchProfile(user.id))
|
||||
|
||||
const [user, config, profile] = await Promise.all([
|
||||
userPromise,
|
||||
fetchConfig(),
|
||||
profilePromise
|
||||
])
|
||||
```
|
||||
|
||||
Reference: [https://github.com/shuding/better-all](https://github.com/shuding/better-all)
|
||||
28
skills/react-best-practices/rules/async-parallel.md
Normal file
28
skills/react-best-practices/rules/async-parallel.md
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
title: Promise.all() for Independent Operations
|
||||
impact: CRITICAL
|
||||
impactDescription: 2-10× improvement
|
||||
tags: async, parallelization, promises, waterfalls
|
||||
---
|
||||
|
||||
## Promise.all() for Independent Operations
|
||||
|
||||
When async operations have no interdependencies, execute them concurrently using `Promise.all()`.
|
||||
|
||||
**Incorrect (sequential execution, 3 round trips):**
|
||||
|
||||
```typescript
|
||||
const user = await fetchUser()
|
||||
const posts = await fetchPosts()
|
||||
const comments = await fetchComments()
|
||||
```
|
||||
|
||||
**Correct (parallel execution, 1 round trip):**
|
||||
|
||||
```typescript
|
||||
const [user, posts, comments] = await Promise.all([
|
||||
fetchUser(),
|
||||
fetchPosts(),
|
||||
fetchComments()
|
||||
])
|
||||
```
|
||||
@@ -0,0 +1,99 @@
|
||||
---
|
||||
title: Strategic Suspense Boundaries
|
||||
impact: HIGH
|
||||
impactDescription: faster initial paint
|
||||
tags: async, suspense, streaming, layout-shift
|
||||
---
|
||||
|
||||
## Strategic Suspense Boundaries
|
||||
|
||||
Instead of awaiting data in async components before returning JSX, use Suspense boundaries to show the wrapper UI faster while data loads.
|
||||
|
||||
**Incorrect (wrapper blocked by data fetching):**
|
||||
|
||||
```tsx
|
||||
async function Page() {
|
||||
const data = await fetchData() // Blocks entire page
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>Sidebar</div>
|
||||
<div>Header</div>
|
||||
<div>
|
||||
<DataDisplay data={data} />
|
||||
</div>
|
||||
<div>Footer</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
The entire layout waits for data even though only the middle section needs it.
|
||||
|
||||
**Correct (wrapper shows immediately, data streams in):**
|
||||
|
||||
```tsx
|
||||
function Page() {
|
||||
return (
|
||||
<div>
|
||||
<div>Sidebar</div>
|
||||
<div>Header</div>
|
||||
<div>
|
||||
<Suspense fallback={<Skeleton />}>
|
||||
<DataDisplay />
|
||||
</Suspense>
|
||||
</div>
|
||||
<div>Footer</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
async function DataDisplay() {
|
||||
const data = await fetchData() // Only blocks this component
|
||||
return <div>{data.content}</div>
|
||||
}
|
||||
```
|
||||
|
||||
Sidebar, Header, and Footer render immediately. Only DataDisplay waits for data.
|
||||
|
||||
**Alternative (share promise across components):**
|
||||
|
||||
```tsx
|
||||
function Page() {
|
||||
// Start fetch immediately, but don't await
|
||||
const dataPromise = fetchData()
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>Sidebar</div>
|
||||
<div>Header</div>
|
||||
<Suspense fallback={<Skeleton />}>
|
||||
<DataDisplay dataPromise={dataPromise} />
|
||||
<DataSummary dataPromise={dataPromise} />
|
||||
</Suspense>
|
||||
<div>Footer</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function DataDisplay({ dataPromise }: { dataPromise: Promise<Data> }) {
|
||||
const data = use(dataPromise) // Unwraps the promise
|
||||
return <div>{data.content}</div>
|
||||
}
|
||||
|
||||
function DataSummary({ dataPromise }: { dataPromise: Promise<Data> }) {
|
||||
const data = use(dataPromise) // Reuses the same promise
|
||||
return <div>{data.summary}</div>
|
||||
}
|
||||
```
|
||||
|
||||
Both components share the same promise, so only one fetch occurs. Layout renders immediately while both components wait together.
|
||||
|
||||
**When NOT to use this pattern:**
|
||||
|
||||
- Critical data needed for layout decisions (affects positioning)
|
||||
- SEO-critical content above the fold
|
||||
- Small, fast queries where suspense overhead isn't worth it
|
||||
- When you want to avoid layout shift (loading → content jump)
|
||||
|
||||
**Trade-off:** Faster initial paint vs potential layout shift. Choose based on your UX priorities.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user