Styled link controls
The markup a styled control may use when it stands in for a catalogue link.
Authoring Contract
A link control is one rendered element that Mokly adapts into a catalogue link while the consumer keeps its component, theme, and styles. It has a React form and an HTML form under one adaptation contract.
In React, MockLink renders an anchor by default. asChild: true adapts one
rendered control:
<MockLink asChild to="account/overview">
<Button tone="primary" onPress={noop}>
Continue
</Button>
</MockLink>This is composition, not React event forwarding. The consumer owns the
component, theme, and styles. A component that requires an onPress to render
enabled still needs that prop; Mokly neither invents a handler nor executes one
in the browser.
Child mode accepts only asChild, to, fragment, and one React element as
children; to takes the complete path, relative path, or definition
reference forms of the authoring contract. Put
classes, styles, ids, accessibility labels, target, and other attributes on
that child. Fragments, text, arrays, missing children, and extra wrapper props
fail rather than silently losing props.
In an HTML screen, component, or page body, to="<path>" with an optional
fragment attribute on a component element, or on a raw a, button, div,
or span, makes that element a link control under the
HTML screens contract. to
takes the complete or relative path form. to and fragment are reserved on
component elements and never reach props, and a component element’s control is
the root element its render produces.
The target and fragment grammars of the
logical link rules apply in every form. The control
may be one HTML a, button, div, or span, with text, icons, and other
non-interactive content. It must be exactly one root element, with no
non-whitespace sibling text. Plain div and span roots support clickable
rows; their default display is kept when they become anchors. CSS should
target classes or stable attributes; a selector that requires the original
element name does not match an active control after it becomes an anchor.
The root may have no role, role="button", or role="link". Other roles,
unsupported or void roots, an editable root, inline event handlers on the
root, nested link controls, and a malformed control boundary fail the build.
Intended focus attributes belong on the root itself.
Placement Tiers
Every ancestor and descendant of the control falls into one of three tiers. An error fails the build. A warning is a build warning: the control still becomes a link and the bytes are identical to a silent placement. A silent placement produces no output. Disabled state does not change an element’s tier.
| Tier | Ancestors | Reason |
|---|---|---|
| error | a, area; an editable element |
The parser splits nested links; editable content captures the click |
| warning | button, label, summary, object; audio/video with controls; a role token of button, link, checkbox, combobox, menuitem, menuitemcheckbox, menuitemradio, option, radio, searchbox, slider, spinbutton, switch, tab, textbox, or treeitem |
One click or key press has two targets; the role may hide children from assistive technology |
| silent | tabindex of any value, including -1 on a programmatic focus target; details content outside summary; a role token of gridcell, listbox, menu, menubar, radiogroup, tablist, tree, or treegrid; every other element |
Not a click target |
| Tier | Descendants | Reason |
|---|---|---|
| error | a, area, button, input, select, textarea, summary, details, label, iframe, object, embed; an editable element; audio/video with controls; the warning-tier ancestor roles; an attribute named on* |
The nested control can take the click, and a details without summary renders one; scripts cannot be static |
| warning | tabindex of any value; the silent-tier ancestor roles |
An extra focus stop or structural role inside one link |
| silent | Every other element | Ordinary content |
An element is editable when its contenteditable attribute is present with
any value other than false. Roles are matched per whitespace-separated
token. An element takes the highest tier reached by any of its features: error,
then warning, then silent. Within that tier, the message shows the first
matching feature in this order: element name, contenteditable, controls,
role, the first on* attribute in authored attribute order, then tabindex.
For example, <button tabindex="0"> is an error named as <button>,
<span role="button" onclick="go()"> is an error named with its role, and
<div role="menu" tabindex="0"> is a warning named with its role.
Checks run in this order: ancestors, the root contract, then descendants. Ancestors are examined from the control outward. The closest error ancestor fails the build even when a warning ancestor is closer; without an error, the closest warning ancestor produces one diagnostic. Descendants are examined in document order. The first error fails the build even when an earlier warning was found; without an error, the first warning produces one diagnostic. One control can therefore produce at most one ancestor and one descendant warning.
Messages describe the classified element as <tag> or
<tag attribute="value">, where the attribute is the one that decided the
tier: role, contenteditable, controls, or tabindex. Displayed authored
values collapse each whitespace run to one space, trim leading and trailing
whitespace, escape " as ", and render C0/C1 terminal controls as
lowercase \uXXXX, so every message stays safe on one line. An on*
attribute is shown by name only, never with its value. Errors carry the
<route>: MockLink child control prefix; an HTML control produces the same
diagnostics with its file, line, and column under the
HTML screens diagnostics. Warning
messages omit the route, which the
build warning record carries.
MockLink child control is inside <a>; move the control outside it
MockLink child control is inside <div contenteditable="">; move the control outside it
MockLink child control is inside <button>; one click or key press has two targets
MockLink child control contains <input>; remove the nested interactive element
MockLink child control contains <span onclick>; remove the inline event handler
MockLink child control contains <span tabindex="-1">; the link has an extra focus stop
MockLink child control contains <div role="menu">; the role does not belong inside a linkA root’s own href or data-nav-href must either be absent or equal the
complete logical destination the control supplies. Conflicting destinations
fail. Native button form attributes are removed during adaptation: a catalogue
link cannot also submit a form. This applies to active and inactive controls.
Ordinary unmarked buttons and metadata-only
data-nav-href references keep their behaviour.
Adaptation
Adaptation applies to screen, component, and page documents alike. In an
HTML body the element that carries to is the control. In a sidecar render,
MockLink asChild wraps the child in a mokly-link marker pair under the
markers rule of the comparison contract; the
core reads the pair into the control’s region and drops it, so no marker is
served. A pair that consumer output authors, and a malformed, unmatched,
nested, or unconsumed pair, fail the build. Attribute names follow HTML’s
case-insensitive parsing rules. Validation reads tree attributes, so a marker
spelling in text, comments, scripts, styles, or unrelated attribute values is
ordinary consumer content.
The data-mokly-link-control attribute namespace is reserved throughout the
document. Consumer output cannot supply these attributes, even without link
controls; only the adapter creates them, and a duplicate attribute on an
adapted element fails even where HTML parsing would collapse it. The composed
document keeps the validated control metadata.
An active control becomes one native HTML anchor. Child content, classes,
inline styles, ids, labels, and other applicable attributes stay intact.
Button roles and button-only form attributes are removed. The logical href
goes through the target, fragment, viewport, scheme, portable URL, and
Browse-marker rules of the
logical link contract and the final
checks of
link validation.
Only a document with an active adapted control receives package-owned CSS, as
one style element in its head. Low-specificity rules restore ordinary
inherited link text styling and the source root’s default display (button:
inline-block, div: block, span and a: inline). Adapted buttons default
to intrinsic fit-content width; authored widths, including full-width block
buttons, take precedence. Consumers that use automatic flex or grid stretching
should state the intended width. Authored CSS and inline styles take
precedence over these defaults. A visible focus outline applies to adapted
links, including controls whose component emits an inline outline reset.
Native browser button chrome and JavaScript-only hover or pressed effects are
not reproduced; use styled static controls. Native links provide keyboard
activation and normal browser link behaviour without a consumer script.
A document without link controls is unchanged by this contract. Serve,
build, and both sides of a comparison see the same adapted document, and
adaptation adds no Changes membership of its own.
Inactive Controls
disabled, aria-disabled="true", aria-busy="true", or inert on the
control keeps it inactive. An enclosing inert, ARIA-disabled or busy container
or disabled fieldset also keeps it inactive; fieldsets are conservatively
treated as disabled even inside their first legend. Disabled or busy child
props must be reflected in rendered HTML for Mokly to observe them.
Inactive roots keep their element, styling, label, and accessibility state;
their href is removed, and a button receives type="button" so it cannot
accidentally submit a form when opened directly. The destination stays only
in inert data-nav-href metadata for the target and fragment validation. No
activatable Browse marker is produced. Mokly does not override a consumer’s
disabled state to make navigation work.
Integration
The package has no consumer component-library or React Native Web runtime dependency. A consumer renderer needs no navigation HTML transformer of its own and keeps theme wrapping and style collection.