MobTranslate styleguide

Preview Card

Offer supplemental destination context on a real link without making hover the only route to essential information.

Use PreviewCard for a useful nonessential summary of a real destination. The link must work independently for mouse, keyboard and touch. Hovering or focusing can disclose context; opening a preview does not mean navigation, permission, loading success or draft saving.

Use Tooltip for brief control explanation, Popover for an explicit interactive supporting task and ordinary visible text for essential help. Keep forms, required recovery and account actions outside this preview. The example supplies persistent description plus an ordinary destination link.

Anatomy and visual specifications

PartInstalled composition
RootDirect installed state owner with no DOM. Caller can control open state or initialize defaultOpen.
TriggerActual anchor/ref with href and useful link text. It is not a button wrapping another anchor. The example puts the actual id on Trigger and provides a 44px link target.
Portal / PositionerDirect portal and styled positioning div/ref, with 8px example side offset/collision padding. Portalled content receives explicit DOM direction.
PopupStyled div/ref, 16px padding, radius-lg and a single 2px readable boundary; max width min(24rem, viewport−16px) and available-height scrolling. No wide shadow or fade.
ArrowAutomatically appended installed Arrow; decorative visual attachment, not an accessible name or extra caller part.
Semantics / focusThe installed preview supplies interaction rather than an automatic modal/dialog form. Example popup is a named supplemental note, with no interactive descendants or focus transfer into it.

A real destination and retained local draft

Hover/focus the actual Accessibility link, inspect its supplemental summary and dismiss it. The local English-only note remains unchanged. Disabling previews closes the disclosure while the ordinary link and persistent help remain available.

Following the link opens the complete real guide. The note belongs to this page’s state; actual document navigation does not promise persistence. The preview uses static local guidance, without remote URL metadata fetching, authenticated permissions or invented source review.

The Accessibility guide contains structure, keyboard, contrast, validation and verification guidance. Follow its link for the complete topic; this supplemental preview does not save the draft below.

Read Accessibility guidance

Actual preview state: closed.

Hover/focus and dismissal keep this page-memory note unchanged. Ordinary link navigation leaves this example; it does not promise draft persistence across a new document.

tsx
'use client';
import {useState,type CSSProperties} from 'react';
import {PreviewCard,PreviewCardTrigger,PreviewCardPortal,PreviewCardPositioner,PreviewCardPopup,Button,Textarea} from '@mobtranslate/ui';
const action:CSSProperties={minWidth:0,maxWidth:'100%',minHeight:44,height:'auto',whiteSpace:'normal',overflowWrap:'anywhere'};
export function PreviewDestinationExample(){
  const [open,setOpen]=useState(false);
  const [enabled,setEnabled]=useState(true);
  const [rtl,setRtl]=useState(false);
  const [note,setNote]=useState('English-only retained local reading note.');
  return <div data-preview-lab className="space-y-4" dir={rtl?'rtl':'ltr'}><div className="flex flex-wrap gap-3"><Button style={action} type="button" variant="outline" aria-pressed={enabled} onClick={()=>{setEnabled(v=>!v);setOpen(false);}}>Enable local destination previews</Button><Button style={action} type="button" variant="outline" aria-pressed={rtl} onClick={()=>setRtl(v=>!v)}>Use RTL destination preview</Button></div>
    <p id="preview-persistent-help">The Accessibility guide contains structure, keyboard, contrast, validation and verification guidance. Follow its link for the complete topic; this supplemental preview does not save the draft below.</p>
    <PreviewCard open={open&&enabled} onOpenChange={next=>setOpen(next&&enabled)}><PreviewCardTrigger id="preview-accessibility-link" href="/guides/accessibility" delay={200} closeDelay={200} aria-describedby="preview-persistent-help" className="underline underline-offset-4" style={{display:'inline-flex',alignItems:'center',minHeight:44,maxWidth:'100%',overflowWrap:'anywhere'}}>Read Accessibility guidance</PreviewCardTrigger><PreviewCardPortal><PreviewCardPositioner sideOffset={8} collisionPadding={8}><PreviewCardPopup role="note" aria-label="Local accessibility topic preview" dir={rtl?'rtl':'ltr'} className={state=>state.open?'destination-preview-open':'destination-preview-closed'}><p className="font-semibold">Accessibility guidance</p><p className="mt-2">Read web requirements and source-backed verification boundaries. This local summary is supplemental; the complete destination is available through the ordinary link.</p><p className="mt-2">No account, permission, remote fetch or draft-save result is inferred from opening this preview.</p></PreviewCardPopup></PreviewCardPositioner></PreviewCardPortal></PreviewCard>
    <label htmlFor="preview-reading-note" className="block">Retained English-only local note</label><Textarea id="preview-reading-note" value={note} onChange={event=>setNote(event.target.value)} /><p data-preview-open-state>Actual preview state: {open&&enabled?'open':'closed'}.</p>
    <p>Hover/focus and dismissal keep this page-memory note unchanged. Ordinary link navigation leaves this example; it does not promise draft persistence across a new document.</p>
  </div>;
}

Complete exported API

Export / propActual ownership
PreviewCardDirect Root: open/defaultOpen=false, onOpenChange(open, details), onOpenChangeComplete and children. Reflect controlled proposals in the parent.
actionsRefInstalled custom unmount ownership. A custom animation integration must complete cleanup; the example does not take over that lifecycle.
PreviewCardTriggerDirect anchor part with href, native/render props, HTMLAnchorElement ref and delay=600ms / closeDelay=300ms defaults. Example uses 200ms for both. Put id/naming on the actual Trigger part when composing a rendered link.
PreviewCardPortalDirect installed portal with native container/mounting options. It is needed in the example’s complete composition.
PreviewCardPositionerStyled installed part/ref with side/align, offsets/collision and native state customization. State class callbacks now compose correctly.
PreviewCardPopupStyled installed div/ref, open/side/align/transition state callbacks and automatic Arrow. Role/name/content policy belongs to the consuming composition.
Availability boundaryThis pin has no generic disabled Root prop. The fixture gates controlled open state separately from whether the destination link works.
Customization boundaryHigh-level exports do not imply a modal focus trap, custom interactive card workflow or automatic URL metadata service. Choose an appropriate component for those tasks.

Hover, focus and dismissal

  • Verify actual delayed mouse hover and keyboard focus disclosure. Do not show an essential instruction only while a pointer is over the link.
  • Escape dismisses. The link keeps its actual name/href and ordinary keyboard activation; preview interaction must not consume navigation or claim a completed destination task.
  • Allow pointer movement from the trigger onto the preview when people need to inspect its content. Test the actual installed pointer transfer, close delay and edge placement.
  • Keep focus on a useful real control. Do not silently place a form or the only recovery action inside a transient nonmodal summary.
  • Disabling only previews must not disable ordinary navigation or discard nearby draft text. Re-enabling is a disclosure policy change, not source availability evidence.

Content, loading and source authority

  • Keep the summary concise, useful and consistent with the actual destination. A preview that contradicts its link creates more work than it saves.
  • If a product fetches metadata, distinguish pending/failed/partial/loaded summaries. A visible panel is not proof that the remote source was read or verified.
  • Keep provenance, freshness and permission decisions tied to real source data. Link title/domain, portrait or hover text do not establish cultural review or authorization.
  • Avoid oversized summaries that require a separate reading workflow. Move long or interactive content to the complete destination or an explicit accessible Popover/Dialog task.
  • Portal direction and scoped CSS tokens may need explicit propagation. Test the popup surface and trigger separately in both themes and narrow layouts.

Migration and acceptance gates

The old page used an independent CSS hover imitation, a placeholder href and an incomplete snippet instead of the exported preview. The guide now composes the actual Root/Trigger/Portal/Positioner/Popup with a real route and checked source.

  • Run test:preview-card, checked-source/token freshness, lint, UI/styleguide types, production build and complete route/fragment crawl. Verify hover/focus, Escape/pointer transfer, actual link navigation, draft retention during disclosure, enabled-preview policy, state classes and geometry/contrast.
  • Hardware touch, spoken assistive-technology, remote metadata races, custom animation ownership and every consuming link composition require separately scoped evidence. DOM checks do not certify all those contexts.