@madebywild/wild-rough-notation
v1.1.0
Published
Create and animate hand-drawn annotations in React
Downloads
467
Maintainers
Readme
wild-rough-notation
Create and animate hand-drawn annotations in React. This package combines two great libraries: rough-notation and react-rough-notation. It was extended to support custom annotations and we added events.
Installation
npm install @madebywild/wild-rough-notationUsage
Basic Example
import { useState } from 'react'
import { RoughNotation } from '@madebywild/wild-rough-notation'
function App() {
const [show, setShow] = useState(true)
return (
<p>
This is a <RoughNotation type="highlight" color="yellow" show={show}>highlighted</RoughNotation> word.
</p>
)
}Annotation Types
underline- Underline the elementunderline-jiggly- Wavy underline with non-uniform frequencyunderline-zigzag- Zigzag underline patternunderline-tick- Underline with a tick mark at the endbox- Draw a box around the elementcircle- Draw a circle around the elementhighlight- Highlight the element (like a marker)strike-through- Strike through the elementcrossed-off- Cross off the element with an Xbracket- Draw brackets around the elementcustom-path- Render a custom SVG pathcustom- Render custom React content (see Custom Annotations below)custom:*- Named custom types (e.g.custom:label,custom:badge) for multiple distinct custom annotations
Group Animations
Use RoughNotationGroup to animate multiple annotations in sequence:
import { useState } from 'react'
import { RoughNotation, RoughNotationGroup } from '@madebywild/wild-rough-notation'
function App() {
const [show, setShow] = useState(false)
return (
<RoughNotationGroup show={show}>
<p>
<RoughNotation type="underline" color="red" order={1}>
First
</RoughNotation>{' '}
then{' '}
<RoughNotation type="circle" color="blue" order={2}>
second
</RoughNotation>{' '}
and finally{' '}
<RoughNotation type="box" color="green" order={3}>
third
</RoughNotation>
</p>
</RoughNotationGroup>
)
}Props
RoughNotation
| Prop | Type | Default | Description |
| --------------------- | ---------------------------------------------------------------- | ---------------- | ------------------------------------------------------- |
| type | string | 'underline' | Type of annotation |
| show | boolean | false | Whether to show the annotation |
| animate | boolean | true | Whether to animate the annotation |
| animationDelay | number | 0 | Delay before animation starts (ms) |
| animationDuration | number | 800 | Duration of the animation (ms) |
| color | string | 'currentColor' | Color of the annotation |
| strokeWidth | number | 1 | Width of the annotation stroke |
| padding | number \| [number, number] \| [number, number, number, number] | 5 | Padding around the element |
| iterations | number | 2 | Number of iterations for the rough effect |
| multiline | boolean | false | Handle multiline text |
| brackets | 'left' \| 'right' \| 'top' \| 'bottom' \| Array | 'right' | Bracket position(s) for bracket type |
| customElement | string | 'span' | HTML element to wrap children |
| order | number | - | Order in group animation |
| getAnnotationObject | (annotation) => void | - | Callback to get the annotation instance |
| customPath | string | - | SVG path string for custom-path type |
| customPathViewBox | { width: number; height: number } | - | ViewBox dimensions for scaling custom paths |
| customRender | ReactNode | - | Custom content to render for custom / custom:* types |
| onAnimationStart | () => void | - | Callback when animation starts |
| onAnimationComplete | () => void | - | Callback when animation completes |
| onRefresh | () => void | - | Callback when annotation is refreshed (e.g., on resize) |
RoughNotationGroup
| Prop | Type | Default | Description |
| ---------- | ----------- | ------- | -------------------------------------------------- |
| show | boolean | false | Whether to show all annotations in sequence |
| children | ReactNode | - | Child elements containing RoughNotation components |
Custom Annotations
The custom type allows you to render any React content as an annotation. This is useful for custom graphics, icons, or complex decorations that can't be achieved with the built-in types.
import { RoughNotation } from '@madebywild/wild-rough-notation'
function App() {
return (
<RoughNotation
type="custom"
show={true}
customRender={
<div className="absolute inset-0 flex items-center justify-center">
<svg viewBox="0 0 100 100" className="w-full h-full">
<circle cx="50" cy="50" r="40" fill="none" stroke="red" strokeWidth="2" />
</svg>
</div>
}
>
Custom annotation
</RoughNotation>
)
}The customRender content is rendered inside an absolutely positioned container that matches the dimensions of the annotated element. The annotation automatically handles browser resize events.
Named Custom Types
When you need multiple distinct custom annotations, use the custom: prefix with any name. This avoids hardcoding a fixed set of custom types and lets you distinguish between them in your application logic.
import { RoughNotation, isCustomType } from '@madebywild/wild-rough-notation'
function App() {
return (
<>
<RoughNotation
type="custom:label"
show={true}
customRender={<LabelAnnotation text="New!" />}
>
Feature
</RoughNotation>
<RoughNotation
type="custom:badge"
show={true}
customRender={<BadgeAnnotation icon="star" />}
>
Premium
</RoughNotation>
</>
)
}Any type matching custom:* behaves identically to custom -- it renders nothing in the core SVG layer and delegates entirely to customRender. The type name is available for you to branch on in your own code.
The exported isCustomType() helper returns true for both 'custom' and any 'custom:*' type:
import { isCustomType } from '@madebywild/wild-rough-notation'
isCustomType('custom') // true
isCustomType('custom:label') // true
isCustomType('underline') // falseCustom Path Annotations
The custom-path type lets you use an SVG path string rendered with the rough/hand-drawn style:
<RoughNotation
type="custom-path"
show={true}
customPath="M0.166992 21.9907L183.167 1.49066L163.667 13.4907"
customPathViewBox={{ width: 184, height: 24 }}
color="red"
>
Custom path underline
</RoughNotation>Advanced Usage
For custom implementations, you can import the core utilities:
import {
annotate,
annotationGroup,
renderAnnotation,
isCustomType,
type RoughAnnotationConfig,
type RoughAnnotation,
} from '@madebywild/wild-rough-notation'
// Create an annotation manually
const element = document.getElementById('my-element')
const annotation = annotate(element, {
type: 'underline',
color: 'red',
animationDuration: 1000,
})
annotation.show()Publishing
This package uses Changesets for version management and publishing. The package is configured as a private package and will be published to npm with restricted access.
Release Workflow
To release a new version, follow these steps:
# 1. Create a changeset describing your changes
npm run changeset
npm run release
this will ask for your npm OTP code and then publish the package to npm.Important Notes
- Private Package: This package is configured with
"publishConfig": { "access": "restricted" }, which means it will be published as a private package on npm. Only users/organizations with access can install it. - 2FA Required: If your npm account has two-factor authentication enabled, you'll need to provide a one-time password (OTP) when publishing. Get the code from your authenticator app and pass it with the
--otpflag. - Version Bumping: The
npm run versioncommand automatically bumps the version based on your changeset (patch, minor, or major).
License
MIT
