Added issues that are going to be tracked and will be deeply considering changes based on DX.

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
2026-05-06 00:21:13 +05:00
parent f6ae86617c
commit 0787525134
8 changed files with 1218 additions and 0 deletions
+525
View File
@@ -0,0 +1,525 @@
# Component API Smells
This document catalogs potential complaints about the current component/template API.
Each item includes:
- Problem
- Why it hurts
- Potential solutions
- Consequences/costs
## 1) Magic Strings for Variants, Sizes, Types, Directions
Problem:
- Many components use raw strings for semantic options (`variant`, `size`, `type`, `direction`, `align`, etc.).
- Invalid values often silently fall back to defaults.
Why it hurts:
- No compile-time safety.
- Typos are easy to miss.
- Weak IntelliSense discoverability.
Potential solutions:
- Replace string options with enums for common semantic domains.
- Generate constants classes per component for non-breaking intermediate step.
- Add analyzers that validate allowed literals where strings are retained.
Consequences/costs:
- Enums can be breaking if public signatures change.
- Constants are low cost but do not fully prevent invalid values.
- Analyzer route adds tooling complexity.
## 2) Inconsistent Styling Extensibility (`extraClasses` and wrappers)
Problem:
- Some components have `extraClasses`; others do not.
- Developers often wrap components in outer `div` just to apply layout/styling.
Why it hurts:
- No consistent mental model.
- Extra wrapper markup increases noise and nesting depth.
Potential solutions:
- Add a standard `className` (or `extraClasses`) parameter to every component.
- Support class merging utility behavior in a shared helper.
Consequences/costs:
- Public constructor expansion across many components.
- Need policy for precedence (base classes first vs custom classes first).
## 3) Missing Uniform Attribute Pass-Through
Problem:
- Attribute extensibility is fragmented (`hxAttrs` in some places, none in others).
- No first-class support for arbitrary `aria-*`, `data-*`, test IDs, analytics attributes.
Why it hurts:
- Manual string composition is error-prone.
- Difficult accessibility and testing instrumentation.
Potential solutions:
- Add a shared attributes bag type (`IReadOnlyDictionary<string, string?>`).
- Keep `hxAttrs` temporarily as compatibility shim.
Consequences/costs:
- Larger refactor surface.
- Slight allocation/processing overhead.
- Requires HTML attribute encoding rules in one central place.
## 4) `hxAttrs` Raw String Footgun
Problem:
- Raw attribute strings allow malformed markup or accidental injection.
Why it hurts:
- Hard-to-debug render bugs.
- Security posture depends on each caller doing manual encoding correctly.
Potential solutions:
- Deprecate raw `hxAttrs` in favor of typed/structured attrs.
- Provide safe helper methods to construct HTMX attribute sets.
Consequences/costs:
- Migration needed for existing call sites.
- Potentially breaking unless a gradual fallback is kept.
## 5) Unsafe-by-Default Raw HTML Content Paths
Problem:
- Several components accept string content that is rendered as raw HTML.
- Caller must remember to encode user-provided values.
Why it hurts:
- XSS risk in real application code.
- Easy to misuse when moving quickly.
Potential solutions:
- Safe-by-default encoding for plain string inputs.
- Separate APIs for encoded text vs trusted HTML (explicit escape hatch).
- Introduce a `SafeHtml` wrapper type for intentional raw HTML.
Consequences/costs:
- Safe-by-default may break current behavior for callers relying on raw HTML.
- Trusted-HTML API adds conceptual complexity, but clearer intent.
## 6) Inconsistent Security Guidance in Component Docs
Problem:
- Some docs mention encoding; others do not provide clear warnings.
Why it hurts:
- Security correctness relies on tribal knowledge.
Potential solutions:
- Add a standardized "Security" section to every component doc.
- Include explicit examples: safe input, unsafe input, and fix.
Consequences/costs:
- Documentation maintenance overhead.
- Strong DX/security benefit for low implementation cost.
## 7) No Explicit "Component Props" Model in Markup
Problem:
- Slots replace placeholders, but there is no direct concept of passing typed props in `.htmx` markup itself.
- Dynamic behavior is mostly constructor-centric in `.htmx.cs`.
Why it hurts:
- Feels unlike modern component systems where props are explicit and local.
- New users may expect inline component parameterization and be surprised.
Potential solutions:
- Document this limitation clearly as a design constraint.
- Add a generated props record convention per component/page.
- Explore optional parameterized slot syntax in generator (long-term).
Consequences/costs:
- Props model requires generator design changes.
- Parameterized slot syntax is high complexity and may conflict with AOT simplicity.
## 8) Constructor Bloat and Low Readability
Problem:
- Some components expose many optional parameters, often multiple strings.
Why it hurts:
- Ambiguous calls and poor self-documentation.
- Easy to mis-order arguments.
Potential solutions:
- Favor required args + options record pattern.
- Add fluent builders for complex components.
Consequences/costs:
- Options records improve readability but introduce extra types.
- Builders can increase allocations and complexity.
## 9) Tuple-Based APIs for Complex Components
Problem:
- Components like tabs/accordion/dropdown/table rely on tuple collections.
Why it hurts:
- Tuples are easy to misuse and harder to read than named objects.
- Harder to evolve APIs without breaking all call sites.
Potential solutions:
- Replace tuple parameters with named records (`TabItem`, `AccordionItem`, etc.).
Consequences/costs:
- Migration churn across existing usage.
- Clear long-term maintainability win.
## 10) Missing Validation Feedback for Invalid Inputs
Problem:
- Invalid option values often degrade silently rather than failing fast.
Why it hurts:
- Bugs are hidden and discovered late.
Potential solutions:
- Add debug-time validation with clear exceptions/messages.
- Optionally emit logs/diagnostics in production with safe defaults.
Consequences/costs:
- Strict runtime validation can be breaking for existing invalid usages.
- Diagnostics-only mode is safer for migration.
## 11) Inconsistent Boolean Option Naming
Problem:
- Different components use varying naming styles for booleans and toggles.
Why it hurts:
- Low API predictability.
Potential solutions:
- Define naming conventions (`isX`, `hasX`, `enableX`) and enforce globally.
Consequences/costs:
- Rename churn if normalized retroactively.
## 12) CSS Contract Coupled to JS via Hidden Class Names
Problem:
- Interactive behavior relies on specific class/data selectors that are effectively API contracts.
Why it hurts:
- Refactoring classes can break behavior.
- Coupling is not obvious from constructor APIs.
Potential solutions:
- Document required selectors/events in each interactive component doc.
- Prefer stable `data-component`/`data-role` markers over purely visual class names.
Consequences/costs:
- Markup updates across components and JS.
- Better long-term resilience to style refactors.
## 13) Runtime Behavior Dependencies Not Surfaced in API
Problem:
- Components requiring JS initialization do not expose that requirement in code signatures.
Why it hurts:
- Silent "renders but does not work" failures.
Potential solutions:
- Add `Requires JavaScript` section in docs and XML comments.
- Add lightweight marker interface or metadata attribute for interactive components.
Consequences/costs:
- Minimal runtime cost; mostly documentation/tooling work.
## 14) Accessibility Ergonomics Gaps
Problem:
- No consistent way to pass `aria-*`, `id`, `for`, `describedby` across all components.
Why it hurts:
- Accessibility quality depends on manual wrapper hacks.
Potential solutions:
- Introduce shared accessible options type.
- Provide defaults and enforce required labels where relevant.
Consequences/costs:
- Constructor changes and additional validation logic.
## 15) Testability Friction (No Standard Test IDs)
Problem:
- No consistent `data-testid` or attribute pass-through strategy.
Why it hurts:
- E2E selectors become brittle (class/text-based selectors).
Potential solutions:
- Add standard attributes bag and testing guidance.
- Add `testId` convenience parameter in interactive/form primitives.
Consequences/costs:
- Minor API surface increase.
- Significant test stability benefit.
## 16) Documentation Discoverability Gaps
Problem:
- Component docs focus on usage but under-emphasize known limitations and smell areas.
Why it hurts:
- New contributors re-learn the same constraints repeatedly.
Potential solutions:
- Add dedicated docs for limitations, anti-patterns, and migration strategy.
- Add an index from component reference into Issues docs.
Consequences/costs:
- Ongoing documentation upkeep.
## 17) Inconsistent Naming (`extraClasses` vs alternatives)
Problem:
- Similar concepts have inconsistent parameter names.
Why it hurts:
- Context switching overhead.
Potential solutions:
- Standardize naming dictionary and enforce in reviews.
- Offer temporary backward-compatible aliases.
Consequences/costs:
- Alias support increases short-term complexity.
## 18) Lack of Strongly-Typed Domain Primitives
Problem:
- IDs, route paths, CSS classes, and labels are all plain strings.
Why it hurts:
- Accidental parameter swaps and weak intent signaling.
Potential solutions:
- Introduce lightweight value objects or records for high-value domains (`DialogId`, `CssClassList`, etc.).
Consequences/costs:
- Added type count and conversion code.
- Better readability and safer APIs.
## 19) Missing Centralized Class Composition Policy
Problem:
- Tailwind class strings are composed ad-hoc in constructors.
Why it hurts:
- Risk of duplicate/conflicting classes.
- Hard to audit variant behavior consistency.
Potential solutions:
- Add shared class composition helper utilities.
- Optionally adopt a deterministic merge utility pattern.
Consequences/costs:
- New utility dependency or internal helper maintenance.
## 20) Limited Error Reporting for Misconfigured Interactive Components
Problem:
- Missing/incorrect JS hooks often fail quietly.
Why it hurts:
- Time-consuming debugging.
Potential solutions:
- Development-only console warnings/assertions from `components.js` when expected markers are missing.
Consequences/costs:
- Slight JS complexity increase.
- Better troubleshooting experience.
## 21) Form Component API Inconsistency
Problem:
- Form primitives vary in how they accept value/default/checked/attrs/labels.
Why it hurts:
- Hard to predict usage patterns across components.
Potential solutions:
- Define a shared form control contract:
- `name`, `id`, `label`, `value`, `disabled`, `required`, `className`, `attributes`
Consequences/costs:
- Widespread API harmonization work.
- Major usability win once stabilized.
## 22) No First-Class Validation/Error State Patterns
Problem:
- Error display, invalid styling, and message linkage are largely ad-hoc.
Why it hurts:
- Inconsistent UX and accessibility for validation states.
Potential solutions:
- Add canonical form-field wrapper component and error semantics.
- Add helper patterns in docs for mapping server validation to components.
Consequences/costs:
- Additional abstractions and migration.
## 23) Table API Lacks Strong Cell/Column Models
Problem:
- Table inputs as nested strings are simplistic and rigid.
Why it hurts:
- Hard to represent links, badges, actions, and per-cell semantics safely.
Potential solutions:
- Introduce column and row models with typed cell renderers.
- Support text cell vs trusted HTML cell explicit APIs.
Consequences/costs:
- Significant redesign effort for table API.
- High payoff for real-world usage.
## 24) Potential Over-Eager Precomputation in Constructors
Problem:
- Some components precompute heavy HTML payloads in constructor.
Why it hurts:
- Allocation spikes for large datasets.
- Can surprise developers expecting render-time streaming.
Potential solutions:
- Lazy compute/cache expensive sections.
- Document performance profile and guardrails per component.
Consequences/costs:
- Possible complexity in caching invalidation.
- Better performance transparency.
## 25) No Compatibility Policy for API Evolution
Problem:
- No explicit deprecation policy for parameter renames or behavior changes.
Why it hurts:
- Contributors hesitate to improve APIs due to break risk.
Potential solutions:
- Define semver/deprecation policy in docs.
- Use staged migration with obsolete annotations.
Consequences/costs:
- Process overhead, but critical for long-term maintainability.
## 26) No Unified Component Design Principles Doc
Problem:
- Patterns are documented, but not as enforceable design principles.
Why it hurts:
- New components may diverge in API style.
Potential solutions:
- Publish a component API style guide with mandatory rules and preferred patterns.
Consequences/costs:
- Requires reviewer discipline.
## 27) Internationalization (i18n) Boundaries Not Explicit
Problem:
- Many labels/content are plain strings without explicit localization guidance.
Why it hurts:
- Inconsistent localization strategy across pages/components.
Potential solutions:
- Add docs for localizable boundaries and resource integration patterns.
Consequences/costs:
- Documentation and integration work.
## 28) Missing "Known Limitations" Section in Component Reference Entry Point
Problem:
- The main component reference does not prominently call out systemic limitations.
Why it hurts:
- Developers discover constraints by trial and error.
Potential solutions:
- Add up-front limitations and issue tracker links in component reference.
Consequences/costs:
- Low cost; immediate discoverability gains.
## 29) API Surface Differs Across Similar Components
Problem:
- Similar categories (display/form/interactive) do not expose comparable extension points.
Why it hurts:
- Surprising differences force re-learning per component.
Potential solutions:
- Define a baseline component contract by category:
- Display: `className`, `attributes`
- Form: baseline form control props + attrs
- Interactive: baseline + JS contract notes
Consequences/costs:
- Requires systematic API audit and staged rollout.
## 30) Missing Tooling Support for API Misuse
Problem:
- No analyzers/code fixes for common mistakes (invalid variant, unsafe content, missing encoding).
Why it hurts:
- Review burden remains manual.
Potential solutions:
- Introduce Roslyn analyzers for:
- magic string validation
- unsafe raw HTML from untrusted sources
- missing serialization registration patterns
Consequences/costs:
- Initial tooling investment is medium-high.
- Scales quality across the codebase after adoption.
---
## Cross-Cutting Improvement Patterns
1. Standardized base options record:
- `className`
- `attributes`
- `testId`
- `ariaLabel`
2. Strong typing for semantic options:
- enums/constants/analyzers
3. Safe content model:
- text-safe by default, explicit trusted-html escape hatch
4. Better docs contract:
- every component doc should include:
- security notes
- accessibility notes
- JS dependency notes (if interactive)
- extension points
5. Migration strategy:
- additive changes first
- obsolete old params
- remove deprecated paths in major version bump