# Laps, Bridles & Dovetails

*Half lap, cross lap, bridle, box joint, through and half-blind dovetail, and sliding dovetail*

**Names:** `half_lap` `cross_lap` `bridle` `box_joint` `dovetail_through` `dovetail_half_blind` `sliding_dovetail` `depth_ratio` `cheek_thickness` `depth` `slope` `pin_count` `finger_width` `socket_depth` `housing_depth` `tongue_thickness` `stopped` `front_gap` `index` `pin` `avoid` `on`

## When to use

- **Members that cross** (frames, lattices, a trestle's X): `half_lap` where at least one
  member ends at the crossing, `cross_lap` where both run on through it, `bridle` where the
  end of one member is forked over the other (an open mortise and tenon).
- **A box or drawer corner** (two boards meeting end to face): `box_joint` for fingers,
  `dovetail_through` or `dovetail_half_blind` for a drawer front.
- **A shelf or divider captured in a housing** (a T, not a corner): `sliding_dovetail`.

The marks, `join` and the layout rule are in `joinery-contract`. A joint with a tenon is in
`joinery-tenons`.

## Syntax

```text
joinery half_lap  name=<N> { [depth_ratio <0..1>]  clearance <mm> }
joinery cross_lap name=<N> { [depth_ratio <0..1>]  clearance <mm> }
joinery bridle    name=<N> { cheek_thickness <mm>  depth <mm>  clearance <mm> }
joinery box_joint name=<N> { pin_count <n> | finger_width <mm>   clearance <mm> }
joinery dovetail_through    name=<N> { slope "1:N"  pin_count <n>   clearance <mm> }
joinery dovetail_half_blind name=<N> { slope "1:N"  pin_count <n>  socket_depth <mm>  clearance <mm> }
joinery sliding_dovetail name=<N> {
    slope "1:N"  housing_depth <mm>  tongue_thickness <mm>  clearance <mm>
    [stopped #true  front_gap <mm>]
}
```

**Crossing members (`half_lap`, `cross_lap`, `bridle`).** The two boards are laid out
**through one another** at full size; the shared box is the joint. The marks sit **on the
cheek**, inside the stock, each facing the board that lies over it.

- `half_lap`/`cross_lap`: each part loses what of the shared box lies on its mark's side,
  down to the cheek. `depth_ratio` (default `0.5`) is the share of the male's thickness it
  loses and **must agree with where its mark puts the cheek**: a disagreement is an error, not
  a guess. `clearance` widens the female's notch but never deepens it, so the cheeks stay
  flush. A `half_lap` reaches the end of at least one member; two boards crossing mid-length
  are a `cross_lap`.
- `bridle`: the female is the fork, open at its end; the male keeps its middle as the tongue.
  `cheek_thickness` is each fork cheek, so the slot is `thickness - 2 * cheek_thickness` wide.
  `depth` is how far the slot runs in from the female's end and must reach across the male.
  **Both members are the same thickness, face for face, and both marks sit on the
  mid-plane** (half the thickness down).

**Corners (`box_joint`, `dovetail_*`).** The male (finger or tail board) is written
**shoulder to shoulder**, like a tenoned rail: its end stops at the female's inner face and
its mark sits on that **end**, facing the female's mark on the **broad face** it bears on. The
compiler adds the fingers or tails to the male's end and cuts the sockets from the female; the
stock list adds the tails to the male's blank.

- Write the female **full length**, running to the male's outer face (a dovetail must end
  there: the tails slide in along the tail board's thickness).
- `box_joint`: `pin_count` is the male's fingers (`2 * pin_count - 1` equal positions, both
  outer ones the male's), **or** `finger_width`, the narrowest finger you want, and the count
  follows the board (an odd number). One of the two, not both.
- `dovetail_*`: `slope "1:N"` is an enum written as a word (1:8 hardwood, 1:6 softwood; outside
  1:4 to 1:12 warns). `pin_count` counts pins **with the half-pin at each edge**, so there is
  one tail fewer. In place of `pin_count`, a graduated schedule: one `pin index=<i> width=<mm>`
  row per tail inside a `repeat`. `socket_depth` (half-blind) is how deep the sockets run into
  the front; the rest is the lap. A through dovetail's tails are as long as the pin board is
  thick. `avoid start=<mm> width=<mm> margin=<mm>` (RFC-0070) keeps every tail root clear of a
  feature cut elsewhere on the same, male board — `start=`/`width=` are offsets from the
  joint's own low end, the same zero a `pin index=` row's position already is, not the
  avoided feature's own `offset=`/`start=` frame (reuse a `dado`'s *run*-axis `start=` to land
  in the same axis). `on=female` (a zone on the pin board instead) is refused, not built.

**`sliding_dovetail`** is a **T**: male mark on the shelf's end, female mark on the broad
face it meets, square. The housing runs the male's whole depth, walls leaning outward as they
go deeper; the male is shouldered down to a matching tapered tongue. `housing_depth` is how
deep it goes and how far the tongue stands, `tongue_thickness` is the tongue's root width
centred on the male mark. `stopped #true` with `front_gap` leaves that much uncut at the end
nearer the project's front (`front=`, default `neg_y`) so the housing does not show.

## Example

A half lap, a cross lap and a bridle. The half lap's marks are on the cheek at mid-thickness
with opposite normals; the bridle's female member ends where the male crosses it.

```kdl
project name="Laps" units=mm schema_version=1 {
    params { glue_clearance 0.2 }
    materials { material Oak type=hardwood }
    // Half lap: each board keeps one half of its thickness at the overlap. The mark of
    // each sits on the cheek (mid-thickness) and faces the other board.
    component LapA role=rail {
        stock Oak 400.0 40.0 20.0
        mark Cheek x=380.0 y=20.0 z=-10.0 normal=neg_z
    }
    component LapB role=rail {
        stock Oak 400.0 40.0 20.0
        mark Cheek x=20.0 y=20.0 z=-10.0 normal=pos_z
    }
    // Cross lap and bridle: members of one thickness, marks on the mid-plane.
    component Bar role=bar {
        stock Oak 300.0 60.0 30.0
        mark Mid x=30.0 y=30.0 z=-15.0 normal=pos_z
        mark MidDown x=30.0 y=30.0 z=-15.0 normal=neg_z
    }
    component Slat role=slat {
        stock Oak 300.0 40.0 20.0
        mark Top x=150.0 y=20.0 z=-10.0 normal=pos_z
        mark Bottom x=150.0 y=20.0 z=-10.0 normal=neg_z
    }
    joinery half_lap name=Lap {
        depth_ratio 0.5
        clearance glue_clearance
    }
    joinery cross_lap name=Cross {
        depth_ratio 0.5
        clearance glue_clearance
    }
    joinery bridle name=Fork {
        cheek_thickness 10.0
        depth 60.0
        clearance glue_clearance
    }
    assembly Frame {
        part Lap_A instance=LapA { by top_sw; pos x=0.0 y=0.0 z=0.0 }
        part Lap_B instance=LapB { by top_sw; pos x=360.0 y=0.0 z=0.0 }
        join joint=Lap male=Lap_A@Cheek female=Lap_B@Cheek

        part Cross_A instance=Slat { by top_sw; pos x=0.0 y=500.0 z=0.0 }
        part Cross_B instance=Slat { by center; pos x=150.0 y=520.0 z=-10.0; rot z=90.0 }
        join joint=Cross male=Cross_A@Top female=Cross_B@Bottom

        part Bridle_A instance=Bar { by top_sw; pos x=0.0 y=1000.0 z=0.0 }
        part Bridle_B instance=Bar { by top_sw; pos x=60.0 y=1000.0 z=0.0; rot z=90.0 }
        join joint=Fork male=Bridle_A@Mid female=Bridle_B@MidDown
    }
}
```

Corners and the housing: the male's mark is on its end, the female's on a broad face.

```kdl
project name="Corners" units=mm schema_version=1 {
    params { glue_clearance 0.2 }
    materials { material Oak type=hardwood }
    component Side role=side {
        stock Oak 20.0 300.0 100.0
        mark Inner x=20.0 y=7.5 z=-50.0 normal=pos_x
    }
    component Front role=front {
        stock Oak 200.0 15.0 100.0
        mark End x=0.0 y=7.5 z=-50.0 normal=neg_x
    }
    joinery box_joint name=Box {
        finger_width 20.0
        clearance glue_clearance
    }
    joinery dovetail_through name=Through {
        slope "1:8"
        pin_count 3.0
        clearance glue_clearance
    }
    joinery dovetail_half_blind name=HalfBlind {
        slope "1:8"
        pin_count 3.0
        socket_depth 12.0
        clearance glue_clearance
    }
    joinery sliding_dovetail name=Housing {
        slope "1:6"
        housing_depth 7.0
        tongue_thickness 10.0
        clearance glue_clearance
        stopped #true
        front_gap 12.0
    }
    assembly Boxes {
        part S1 instance=Side { by top_sw; pos x=0.0 y=0.0 z=0.0 }
        part F1 instance=Front { by top_sw; pos x=20.0 y=0.0 z=0.0 }
        part S2 instance=Side { by top_sw; pos x=0.0 y=400.0 z=0.0 }
        part F2 instance=Front { by top_sw; pos x=20.0 y=400.0 z=0.0 }
        part S3 instance=Side { by top_sw; pos x=0.0 y=800.0 z=0.0 }
        part F3 instance=Front { by top_sw; pos x=20.0 y=800.0 z=0.0 }
        part S4 instance=Side { by top_sw; pos x=0.0 y=1200.0 z=0.0 }
        part F4 instance=Front { by top_sw; pos x=20.0 y=1200.0 z=0.0 }
        join joint=Box male=F1@End female=S1@Inner
        join joint=Through male=F2@End female=S2@Inner
        join joint=HalfBlind male=F3@End female=S3@Inner
        join joint=Housing male=F4@End female=S4@Inner
    }
}
```

## Pitfalls and diagnostics

- **A lap whose `depth_ratio` disagrees with its mark** is refused; so is a mark off the shared
  cheek plane (`contact`), marks that do not face each other (`facing`), and members that cross
  skewed rather than square to one another (`faces-overlap`: skewed laps are unsupported,
  RFC-0022).
- **A `half_lap` crossing mid-length of both members** is refused with "this is a
  `cross_lap`"; a `bridle` whose female does not end where the male crosses it is refused with
  "a member crossing in its middle is a `cross_lap`" (`kind`).
- **A bridle** needs members the same thickness, face for face (`material`), marks half the
  thickness down (`contact`), `cheek_thickness` leaving a slot (`params`), and `depth` reaching
  across the male and not running the female's whole length (`remaining-wall`).
- **Corners.** Refused: the male mark on a broad face, the female mark on an end or edge, the
  male's end not wholly on the face, fewer than three fingers, a finger, tail root or pin
  under 3 mm, a half-blind socket as deep as the pin board is thick, a box joint with both or
  neither of `pin_count` and `finger_width`, dovetail sockets closed on both sides. Warned:
  a lap thinner than `max(3 mm, 15 %)`, a fractional `pin_count`, a slope outside 1:4 to 1:12.
- `slope "1:8"` is a word, never a parameter's name. Do not give a `dovetail_*` both
  `pin_count` and a `pin` schedule.
- A lipped (overlay) drawer front that runs past the side: rabbet its back face back to the
  side at least as deep as the sockets (`bedside_cabinet.kdl`).
- A groove through the male's end stops at the shoulder; run it on into the tails with
  `stopped=#true` where something (a drawer floor) sits in it (`blanket_chest.kdl`).
- Choose the joint a woodworker would use: the validator cannot tell a poor choice (box joints
  on a corner that needs a mitre) from a good one.

## See also

- `docs/KDL_AUTHORING.md` §8 ("Tenons, laps, bridles and mitres", "Box joints and dovetails",
  "Sliding dovetail housing"); ADR-0013 (and its lap addendum), ADR-0015 (graduated
  schedules); RFC-0124 (sliding dovetail).
- `examples/desk.kdl` (`box_joint`), `examples/dining_chair.kdl` (`half_lap`),
  `examples/panel_door_and_window.kdl` (`cross_lap`), `examples/bedside_cabinet.kdl`,
  `examples/blanket_chest.kdl`, `examples/biedermeier_box.kdl`, `examples/welsh_dresser.kdl`
  (dovetails), `examples/bookcase.kdl` (`sliding_dovetail`).
- `joinery-contract.md`, `joinery-tenons.md`, `joinery-frames.md`, `edge-and-face-cuts.md`.

## Names

Every name this page declares, how the example library uses it, and the shortest real use.

### `half_lap`

joinery kind; value in 4 of 116 examples (4 uses).

Shortest real use, from `pierced_splat_side_chair.kdl`:

```kdl
joinery half_lap name=SlipSeatCornerJoint {
    depth_ratio 0.5
    clearance glue_clearance
}
```

### `cross_lap`

joinery kind; value in 3 of 116 examples (3 uses).

Shortest real use, from `mid_century_slat_bench.kdl`:

```kdl
joinery cross_lap name=SlatLap {
    depth_ratio 0.25
    clearance glue_clearance
}
```

### `bridle`

joinery kind; not used by any example yet.

### `box_joint`

joinery kind; value in 6 of 116 examples (7 uses).

Shortest real use, from `danish_teak_sideboard.kdl`:

```kdl
joinery box_joint name=CaseCorner {
    finger_width finger_w
    clearance glue_clearance
}
```

### `dovetail_through`

joinery kind; value in 10 of 116 examples (10 uses).

Shortest real use, from `arts_crafts_cabinet_on_stand.kdl`:

```kdl
joinery dovetail_through name=CarcaseDovetail {
    slope "1:8"
    pin_count 5
    clearance glue_clearance
}
```

### `dovetail_half_blind`

joinery kind; value in 17 of 116 examples (17 uses).

Shortest real use, from `welsh_dresser.kdl`:

```kdl
joinery dovetail_half_blind name=DrawerDovetail {
    slope "1:6"
    pin_count 3.0
    socket_depth 10.0
    clearance glue_clearance
}
```

### `sliding_dovetail`

joinery kind; value in 8 of 116 examples (8 uses).

Shortest real use, from `studio_cabinet_on_stand.kdl`:

```kdl
joinery sliding_dovetail name=ShelfDovetail {
    slope "1:8"
    housing_depth "case_th * 0.4"
    tongue_thickness "shelf_th * 0.6"
    clearance glue_clear
}
```

### `depth_ratio`

node in 7 of 116 examples (7 uses).

Shortest real use, from `dining_chair.kdl`:

```kdl
depth_ratio 0.5
```

### `cheek_thickness`

not used by any example yet.

### `depth`

property; node in 59 of 116 examples (122 uses); property in 95 of 116 examples (571 uses); value in 33 of 116 examples (74 uses).

Shortest real use, from `glazed_vitrine_whiplash_crest.kdl`:

```kdl
depth 8.0
```

### `slope`

word-valued property; node in 23 of 116 examples (35 uses).

Shortest real use, from `art_deco_cabinet_on_fuseau_legs.kdl`:

```kdl
slope "1:8"
```

### `pin_count`

node in 21 of 116 examples (28 uses).

Shortest real use, from `arts_crafts_cabinet_on_stand.kdl`:

```kdl
pin_count 5
```

### `finger_width`

node in 4 of 116 examples (5 uses).

Shortest real use, from `danish_teak_sideboard.kdl`:

```kdl
finger_width finger_w
```

### `socket_depth`

node in 17 of 116 examples (18 uses); value in 1 of 116 examples (1 uses).

Shortest real use, from `welsh_dresser.kdl`:

```kdl
socket_depth 10.0
```

### `housing_depth`

node in 11 of 116 examples (15 uses); value in 7 of 116 examples (16 uses).

Shortest real use, from `pennsylvania_schrank.kdl`:

```kdl
housing_depth    8.0
```

### `tongue_thickness`

node in 12 of 116 examples (18 uses); value in 3 of 116 examples (3 uses).

Shortest real use, from `dining_table.kdl`:

```kdl
tongue_thickness 10.0
```

### `stopped`

property; node in 7 of 116 examples (7 uses); property in 40 of 116 examples (135 uses).

Shortest real use, from `biedermeier_chest_of_drawers.kdl`:

```kdl
stopped #true
```

### `front_gap`

node in 7 of 116 examples (8 uses); value in 1 of 116 examples (1 uses).

Shortest real use, from `danish_teak_sideboard.kdl`:

```kdl
front_gap 10.0
```

### `index`

property; property in 1 of 116 examples (1 uses).

Shortest real use, from `blanket_chest.kdl`:

```kdl
pin index=i width="i == 2.0 ? 18.0 : (carcass_height - carcass_th) / 8.0"
```

### `pin`

node in 19 of 116 examples (35 uses).

Shortest real use, from `appalachian_ladder_back_side_chair.kdl`:

```kdl
pin shape=round size=3.0
```

### `avoid`

node in 1 of 116 examples (1 uses).

Shortest real use, from `blanket_chest.kdl`:

```kdl
avoid start="carcass_height - till_lid_th - till_height" width=till_stock_th
```

### `on`

word-valued property; not used by any example yet.

## Related terms

- [Box joint](/almanac/techniques#box-joint)
- [Bridle joint](/almanac/techniques#bridle-joint)
- [Cross-lap joint](/almanac/techniques#cross-lap-joint)
- [Dado](/almanac/techniques#dado)
- [Half-blind dovetail](/almanac/techniques#half-blind-dovetail)
- [Half-lap joint](/almanac/techniques#half-lap-joint)
- [Peg](/almanac/techniques#peg)
- [Sliding dovetail](/almanac/techniques#sliding-dovetail)
- [Swallowtail fingers](/almanac/techniques#swallowtail-fingers)
- [Through dovetail](/almanac/techniques#through-dovetail)
