# Ram A design-tool selection effect for React. Ram (Swedish for "frame", pronounced /rɑːm/, like "rahm", rhyming with "calm") is a tiny React component for animated design-tool selection frames around text. Wrap your text and Ram handles the measurement, selection outline, corner handles and animation. - Docs: https://github.com/mirayavandiepen/ram/docs - Source: https://github.com/mirayavandiepen/ram ## Installation ```bash pnpm add @mirayavandiepen/ram ``` Also on npm, yarn and bun: `npm install @mirayavandiepen/ram`, `yarn add @mirayavandiepen/ram`, `bun add @mirayavandiepen/ram`. ## Usage Wrap the text you want to frame. ```tsx import { Ram } from "@mirayavandiepen/ram"; export default function Example() { return (

built to delight

); } ``` ## Appearance Every part of the frame is optional. Drawn in selection blue by default, or in any colour you name. ```tsx delight delight delight delight delight delight delight delight ``` ## Measurement label The label reports the rendered width and height of the text, in CSS pixels. It always corresponds to what is actually on the page, and re-measures if the text ever resizes. ```tsx delight delight `w ${Math.round(width)}`}>delight ``` ## Label position Centred positions stay pinned to the resting centre of the frame while the text breathes, so the number never slides. ```tsx delight ``` One of `top`, `bottom`, `top-left`, `top-right`, `bottom-left` or `bottom-right`. ## Animation Control how the frame enters, moves and leaves. The tracking pass loosens the letter-spacing, tightens past where it started, and settles back, without ever moving the text around it. ```tsx delight ``` ## Trigger Decide when the sequence plays. On mount is the default; hover and click need nothing else wired up, and a controlled frame follows a boolean. ```tsx delight hover me click me ``` ```tsx const [active, setActive] = useState(false); delight ``` ## Styling Ram inherits everything typographic from its parent. It sets no font size, family, weight or line height of its own, so it drops into any heading and follows it at every breakpoint. ```tsx

built to delight

``` The chrome is tuned with CSS variables, set on the component or any ancestor. | Variable | Default | Description | | --- | --- | --- | | `--ram-color` | `#0d99ff` | Colour of the outline, handles and label. | | `--ram-line-width` | `1px` | Thickness of the outline. | | `--ram-outline-opacity` | `0.6` | How far the outline is held below full strength. The handles stay solid. | | `--ram-handle-size` | `6px` | Width and height of each corner handle. | | `--ram-handle-fill` | `light-dark(#fff, #111)` | Centre of a hollow handle. Ignored when handles are solid. | | `--ram-handle-line-width` | `1px` | Thickness of the handle rings, set apart from the outline so a heavy stroke never closes a hollow handle up. | | `--ram-inset` | `4px` | How far the outline sits outside the text. | | `--ram-label-background` | `currentColor` | Background of the label, which follows the frame colour. | | `--ram-label-color` | `light-dark(#fff, #111)` | Label text: white on light schemes, near-black on dark. | | `--ram-label-font-size` | `11px` | Label text size. | | `--ram-label-offset` | `6px` | Gap between the label and the outline. | ```tsx delight ``` ## API | Prop | Type | Default | Description | | --- | --- | --- | --- | | `children` | `ReactNode` | | The text to frame. Required. | | `delay` | `number` | `0` | Milliseconds to wait before the frame appears. | | `duration` | `number` | `1400` | Length of the tracking pass, in milliseconds. | | `holdDuration` | `number` | `1200` | How long the frame holds before leaving, in milliseconds. | | `label` | `boolean \| (size) => string` | `true` | Show the width × height label, or format it yourself. | | `labelPosition` | `"top" \| "bottom" \| "top-left" \| "top-right" \| "bottom-left" \| "bottom-right"` | `"top"` | Where the label sits. | | `handles` | `boolean` | `true` | Draw the four corner handles. | | `handleSize` | `number` | `6` | Width and height of each corner handle, in px. | | `handleFill` | `"hollow" \| "solid"` | `"hollow"` | Ring each handle around the page colour, or fill it with the frame colour. | | `handleBackground` | `string` | `light-dark(#fff, #111)` | What a hollow handle is filled with. Ignored when handles are solid. | | `lineWidth` | `number` | `1` | Thickness of the outline, in px. | | `handleLineWidth` | `number` | `1` | Thickness of the handle rings, in px. Independent of lineWidth, the way a design tool keeps a handle hairline however heavy the stroke is. | | `animation` | `"tracking" \| "static"` | `"tracking"` | Whether the letter-spacing breathes while the frame is up. | | `trigger` | `"mount" \| "hover" \| "click" \| "manual"` | `"mount"` | What starts the sequence. Becomes "manual" when active is set. | | `active` | `boolean` | | Controlled visibility. Implies trigger="manual". | | `persistent` | `boolean` | `false` | Keep the frame on the page instead of leaving after the hold. | | `color` | `string` | `#0d99ff` | Colour of the outline, handles and label. Any CSS colour; selection blue by default. | | `labelColor` | `string` | `light-dark(#fff, #111)` | Colour of the text inside the label. | | `labelBackground` | `string` | `currentColor` | Background of the label chip. Follows the frame colour. | | `className` | `string` | | Applied to the wrapping span. | | `style` | `CSSProperties` | | Applied to the wrapping span. A good place for CSS variables. | | `onStart` | `() => void` | | Called when the frame starts to appear. | | `onComplete` | `() => void` | | Called when the frame has left, or has settled when persistent. | ## Accessibility - The outline, handles and label are decorative and hidden from assistive technology. - The wrapped text stays ordinary text: selectable, searchable, and read exactly as written. - `prefers-reduced-motion` drops the tracking pass. The frame still fades in and out. - Ram adds no headings, landmarks or roles, so the semantics of the surrounding markup are unchanged.