component
Signpost
A wooden post-and-plank signboard carrying a name in the world's display font; the post sits behind the board so the text reads clean.
⌖ Explore the library in 3D → · View as Markdown
Add it
npx @runek/cli add signpost
Pulls sign, @react-three/rapier@^2.2.0, @runek/core@^0.15.0.
Use it
import { Signpost } from './runek/Signpost'
<Signpost position={[0, 0, 0]} />
Or as a node in a world file (worlds as data):
{ "type": "Signpost", "props": { "position": [0, 0, 0] } }
Props
| Prop | Type | Default | Description |
|---|---|---|---|
name |
string |
"" |
The name shown on the board. A plain string, not children: a world renderer overrides a node’s children prop with its nested nodes, so text authored in JSON must be a named prop to survive. Empty by default — <Signpost /> is a blank board. |
height |
number |
2.6 |
Post height, in units. |
width |
number |
3.4 |
Board width, in units. |
color |
color |
Post + board wood color; defaults to the palette’s wood. |
|
textColor |
color |
Name color; defaults to the palette’s sand (legible on the wood). |
|
size |
number |
0.5 |
Name cap height, in units. |
position |
[x, y, z] |
[0, 0, 0] |
Position [x, y, z] in units (1 unit = 1 m, Y-up). |
rotation |
[x, y, z] |
[0, 0, 0] |
Euler rotation [x, y, z] in radians. |
seed |
number |
Accepted for the component contract; this component has no seeded variation yet. |
Registry manifest
registry manifest /r/components/signpost.json Self-contained JSON: inlined source plus resolved dependencies, exactly whatrunek add signpost fetches.
Browse the whole catalog in the gallery →.
Source (Signpost.tsx)
import { CuboidCollider, RigidBody } from '@react-three/rapier'
import { useWorld, type WorldComponentProps } from '@runek/core'
import { Sign } from './Sign'
export interface SignpostProps extends WorldComponentProps {
/**
* The name shown on the board. A plain string, **not** `children`: a world renderer overrides a
* node's `children` prop with its nested nodes, so text authored in JSON must be a named prop to
* survive. Empty by default — `<Signpost />` is a blank board.
*/
name?: string
/** Post height, in units. */
height?: number
/** Board width, in units. */
width?: number
/** Post + board wood color; defaults to the palette's `wood`. */
color?: string
/** Name color; defaults to the palette's `sand` (legible on the wood). */
textColor?: string
/** Name cap height, in units. */
size?: number
}
/**
* A roadside signboard: a wooden post and a plank carrying a name in the world's display font.
* The post sits **behind** the plank so it never crosses the text, and the board is single-sided
* (its front faces local +Z) — rotate it to face the reader. The post is the only collider.
*/
export function Signpost({
name = '',
position = [0, 0, 0],
rotation = [0, 0, 0],
height = 2.6,
width = 3.4,
color,
textColor,
size = 0.5,
}: SignpostProps) {
const { unit, palette } = useWorld()
const wood = color ?? palette.wood
const ink = textColor ?? palette.sand
const H = height * unit
const W = width * unit
const boardH = 0.95 * unit
const boardY = H - boardH / 2
const boardD = 0.12 * unit
const postR = 0.1 * unit
// The post sits wholly behind the plank (overlapping it a touch) so it never crosses the front
// face where the name is drawn.
const postZ = -(boardD / 2 + postR - 0.03 * unit)
const boardZ = boardD / 2 + 0.02 * unit
return (
<RigidBody type="fixed" colliders={false} position={position} rotation={rotation}>
<CuboidCollider args={[postR, H / 2, postR]} position={[0, H / 2, postZ]} />
{/* post — behind the plank, clear of the text */}
<mesh position={[0, H / 2, postZ]} castShadow>
<cylinderGeometry args={[postR, postR, H, 8]} />
<meshStandardMaterial color={palette.woodDark} roughness={0.85} />
</mesh>
{/* plank */}
<mesh position={[0, boardY, 0]} castShadow receiveShadow>
<boxGeometry args={[W, boardH, boardD]} />
<meshStandardMaterial color={wood} roughness={0.8} />
</mesh>
{/* the name, on the clear front face */}
{name && (
<Sign position={[0, boardY, boardZ]} size={size} variant="display" color={ink}>
{name}
</Sign>
)}
</RigidBody>
)
}
Signpost.groundSitting = true