diff --git a/crates/pdf/src/forms.rs b/crates/pdf/src/forms.rs
--- a/crates/pdf/src/forms.rs
+++ b/crates/pdf/src/forms.rs
@@ -10,8 +10,9 @@ use designcraft_doc::{Document, FieldKind, FormField, Item, MediaOptions};
 use crate::export::Sheet;
 use crate::transitions::{bracketed, int_after, object, rfind};
 
-/// A field placed on an output page: (page index, PDF rectangle, field).
-pub(crate) type Placed = (usize, [f64; 4], FormField);
+/// A field placed on an output page: (page index, PDF rectangle, field, border width, 0 when the
+/// item has no stroke).
+pub(crate) type Placed = (usize, [f64; 4], FormField, f64);
 
 /// A placed video or sound on an output page: (page index, PDF rectangle, file name, MIME type,
 /// bytes, options).
@@ -37,7 +38,8 @@ pub(crate) fn collect(doc: &Document, sheets: &[Sheet]) -> Vec<Placed> {
     let mut out = Vec::new();
     visit(doc, sheets, |pi, r, it| {
         if let Some(f) = &it.form_field {
-            out.push((pi, r, f.clone()));
+            let bw = if it.stroke.is_none() || !it.stroke.weight.is_finite() { 0.0 } else { it.stroke.weight };
+            out.push((pi, r, f.clone(), bw));
         }
     });
     out
@@ -158,13 +160,14 @@ pub(crate) fn add_fields(pdf: &[u8], fields: &[Placed], media: &[PlacedMedia]) -
         put(&mut out, font, b"<</Type/Font/Subtype/Type1/BaseFont/Helvetica/Encoding/WinAnsiEncoding>>");
     }
     let mut all = Vec::new();
-    for (page, r, f) in fields {
+    for (page, r, f, bw) in fields {
         let Some(&page_ref) = kids.get(*page) else { continue };
         let rect = format!("[{:.2} {:.2} {:.2} {:.2}]", r[0], r[1], r[2], r[3]);
         let (w, h) = (r[2] - r[0], r[3] - r[1]);
         let fs = if f.font_size > 0.0 { f.font_size } else { 0.0 };
+        let (bc, bs) = if *bw > 0.0 { ("/BC[0.45 0.45 0.45]", format!("/BS<</W {bw:.2}>>")) } else { ("", "/BS<</W 0>>".to_string()) };
         let common = format!(
-            "/Type/Annot/Subtype/Widget/T{}/Rect{rect}/F 4/P {page_ref} 0 R/MK<</BC[0.45 0.45 0.45]/BG[1 1 1]>>/DA(/Helv {fs:.1} Tf 0 g)",
+            "/Type/Annot/Subtype/Widget/T{}/Rect{rect}/F 4/P {page_ref} 0 R/MK<<{bc}/BG[1 1 1]>>{bs}/DA(/Helv {fs:.1} Tf 0 g)",
             pdf_string(&f.name)
         );
         let req = if f.required { 2 } else { 0 };
@@ -175,7 +178,8 @@ pub(crate) fn add_fields(pdf: &[u8], fields: &[Placed], media: &[PlacedMedia]) -
             }
             FieldKind::CheckBox => {
                 // Appearances: an empty box, and a box with a check mark.
-                let border = format!("0.45 G 1 w 0.5 0.5 {:.2} {:.2} re S", (w - 1.0).max(0.0), (h - 1.0).max(0.0));
+                let border =
+                    if *bw > 0.0 { format!("0.45 G 1 w 0.5 0.5 {:.2} {:.2} re S", (w - 1.0).max(0.0), (h - 1.0).max(0.0)) } else { String::new() };
                 let check = format!(
                     "{border} 0 g {:.2} w 1 J 1 j {:.2} {:.2} m {:.2} {:.2} l {:.2} {:.2} l S",
                     (w.min(h) * 0.12).max(0.8),
diff --git a/crates/pdf/src/tests.rs b/crates/pdf/src/tests.rs
--- a/crates/pdf/src/tests.rs
+++ b/crates/pdf/src/tests.rs
@@ -791,6 +791,36 @@ fn only_printed_fields_and_buttons_export() {
     assert_eq!(all.matches("/Link").count(), shown.matches("/Link").count(), "only the shown button");
 }
 
+#[test]
+fn form_field_border_follows_the_item_stroke() {
+    use designcraft_doc::{FieldKind, FormField, Shape, Stroke};
+    use designcraft_geom::shapes::rectangle;
+    let export = |stroke: Stroke| {
+        let mut d = Document::new(&NewDocument::default());
+        let lid = d.default_layer();
+        let mut it = Item::new(ItemId(d.alloc()), lid, Shape::Rectangle, rectangle(Rect::new(72.0, 72.0, 250.0, 96.0)));
+        it.stroke = stroke;
+        it.form_field = Some(FormField {
+            kind: FieldKind::TextField,
+            name: "name".into(),
+            value: String::new(),
+            options: Vec::new(),
+            multiline: false,
+            required: false,
+            font_size: 0.0,
+        });
+        d.insert_item(SpreadRef::Doc(0), it, None).unwrap();
+        uncompressed(&d, PdfOptions::default())
+    };
+    let none = export(Stroke { weight: 0.0, swatch: designcraft_color::swatch::NONE.into(), ..Stroke::default() });
+    assert!(none.contains("/Subtype/Widget"));
+    assert!(none.contains("/BS<</W 0>>"), "no border width");
+    assert!(!none.contains("/BC"), "no border colour");
+    let stroked = export(Stroke { weight: 1.0, ..Stroke::default() });
+    assert!(stroked.contains("/BS<</W 1"), "border width");
+    assert!(stroked.contains("/BC"), "border colour");
+}
+
 #[test]
 fn extreme_page_selection_is_a_page_error_without_overflow() {
     let doc = doc_with_text("Bounded page selection");
