Telogen Effluvium
"telogen effluvium"[MeSH Terms] AND diagnosis
telogen effluvium hair loss clinical

This composite of four images demonstrates clinical and diagnostic findings of Hair Loss After SARS-CoV-2 Infection (HLASCI), characterized as telogen effluvium (TE). (a) Clinical photograph of the frontal scalp of a 57-year-old female showing diffuse thinning and decreased hair density with visible short, regrowing hairs at the hairline. (b) Trichoscopic image (magnified view) of the scalp showing upright regrowing hairs alongside vacant follicular ostia (yellowish dots), which represent empty follicles in the telogen phase. (c) Photomicrograph of shed hairs highlighting terminal hair shafts with tapered tips. (d) Clinical photograph showing clumps of shed hairs with bulbous, club-shaped roots characteristic of telogen hair follicles. Collectively, these images illustrate the typical progression and diagnostic features of post-viral telogen effluvium, including the synchronization of the hair cycle into the shedding phase and subsequent early regrowth. The presentation is relevant for dermatology education regarding secondary hair loss following systemic inflammatory stress or 'cytokine storms' associated with COVID-19.

Imaging modality: Clinical photography; macroscopic close-up of hair fragments collected from the scalp to illustrate anagen effluvium. Primary subject: Hair shafts showing abrupt shedding during the anagen phase. Specimen: Hair sample (scalp) arranged on a blue background for contrast. Perspective: Macroscopic, top-down view focusing on hair shaft morphology. Anatomical location: Scalp hair shafts as the source of the specimens. In terms of technique, high-resolution digital photography was used with diffuse lighting to minimize glare; no staining or contrast agents were applied. Magnification: Macro level (approximately 2–5x) to reveal hair shaft integrity and cuticle; acquisition parameters not provided. Observed features: Dark, bundled hair shafts with varying lengths, some with abrupt ends and frayed cuticles; minimal to no root visible. The sample demonstrates a rapid shedding pattern typical of anagen effluvium. The image documents anagen effluvium—a rapid loss of growing hair often linked to chemotherapy or toxin exposure. Notable features include uniform shedding of anagen hairs with preserved follicular openings and minimal inflammation on the specimen level. Diagnostic significance: Supports diagnosis of drug-induced hair loss and distinguishes from telogen effluvium or alopecia areata. Differential considerations: Telogen effluvium, chemotherapy-related damage, radiation-induced hair loss. Clinical correlation: Useful in dermatology, oncology, and trichology to assess severity, monitor response to cytotoxic therapy, and guide counseling.

This composite clinical photograph illustrates the 'acute telogen effluvium triad' following resolution, specifically in a patient post-weight loss. Image A (frontal view) demonstrates a 'frontal fringe' composed of short, regrowing hairs along the anterior hairline. Image B (lateral view) shows bitemporal rarefaction, characterized by a visible decrease in hair density and thinning of hair shafts in the temporal region, making the scalp more prominent. Image C (posterior view) displays an 'occipital fringe,' where hair regrowth is evident through varying hair lengths and thicknesses in the occipital region. Collectively, these images highlight the characteristic clinical signs of hair recovery after a telogen effluvium event. The material is intended for intermediate to advanced medical learners in dermatology, demonstrating the differential diagnosis between active hair loss and clinical signs of recovery/regrowth.

This clinical photograph provides a superior view of the human scalp, demonstrating the clinical presentation of telogen effluvium during the recovery phase. The patient exhibits dark, terminal hair coverage across the majority of the scalp. While hair density has significantly improved, visible thinning remains prominent at the vertex and mid-scalp regions, where the underlying scalp skin is partially visible through the hair shafts. This diffuse thinning pattern is characteristic of systemic stress-induced hair loss, such as that following severe viral infection or hospitalization. The image illustrates the gradual process of hair follicle re-entry into the anagen phase, showing substantial regrowth and volume restoration compared to acute stages of alopecia. This visual is an important educational resource for dermatology and internal medicine, highlighting the natural history and favorable prognosis of post-inflammatory telogen effluvium.
| Mechanism | Description | Example |
|---|---|---|
| Immediate anagen release | Abrupt, premature conversion of anagen → telogen | Febrile illness, childbirth, surgery |
| Delayed anagen release | Prolonged anagen (as in pregnancy), mass release after trigger resolves | Postpartum TE, 2-3 months after delivery |
| Short anagen | Intrinsically shortened anagen phase; more hairs cycling into telogen constantly | Chronic TE, androgenetic alopecia |
| Immediate telogen release | Premature exit from telogen to initiate anagen | Topical minoxidil (transient early shedding) |
| Delayed telogen release | Prolonged telogen; synchronous delayed shedding | Seasonal shedding |



| Condition | Distinguishing Features |
|---|---|
| Androgenetic alopecia | Patterned distribution; miniaturization on biopsy |
| Diffuse alopecia areata | Personal/family hx of AA; nail changes; body hair loss |
| Central centrifugal cicatricial alopecia | Vertex scarring; women of African descent |
| Frontal fibrosing alopecia | Band-like frontotemporal scarring; eyebrow loss |
| Traction alopecia | History of tight braiding/tension |
| Hypotrichosis simplex | Early childhood onset; progressive; no skin/nail changes |
| Feature | Acute TE | Chronic TE (CTE) |
|---|---|---|
| Duration | <6 months | >6 months, fluctuating |
| Trigger | Identifiable (illness, surgery, delivery) | Idiopathic |
| Demographics | Any age, gender | Women, 4th-6th decade |
| Mechanism | Premature anagen release | Shortened anagen phase |
| Pull test | Diffusely positive | Positive at vertex/occiput |
| Treatment | Treat cause; reassure | 5% minoxidil |
| Prognosis | Excellent, full regrowth | Fluctuating; may persist |
Create a clinical reference PDF on telogen effluvium diagnosis and management
mkdir -p /tmp/workspace/telogen-effluvium && ls /tmp/workspace/telogen-effluvium/
~/telogen-effluvium/generate_pdf.py
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether, ListFlowable, ListItem, PageBreak
)
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import inch, mm
from reportlab.lib import colors
from reportlab.lib.pagesizes import A4
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
from reportlab.platypus.flowables import Flowable
import reportlab.rl_config
OUTPUT = "/tmp/workspace/telogen-effluvium/Telogen_Effluvium_Clinical_Reference.pdf"
# ── Colour palette ────────────────────────────────────────────────────────────
C_NAVY = colors.HexColor("#1B3A5C") # header / title bar
C_TEAL = colors.HexColor("#1A7B8C") # section heading bar
C_TEAL_LITE = colors.HexColor("#E8F5F7") # section heading bar fill (light)
C_ACCENT = colors.HexColor("#D4A843") # gold accent line
C_RED = colors.HexColor("#C0392B") # alert / warning
C_GREEN = colors.HexColor("#27AE60") # positive/good
C_AMBER = colors.HexColor("#E67E22") # caution
C_BG_GREY = colors.HexColor("#F4F6F8") # table alt row
C_BG_BLUE = colors.HexColor("#EBF4F8") # info box
C_TEXT = colors.HexColor("#2C3E50") # body text
C_WHITE = colors.white
C_HEADER_TXT= colors.HexColor("#E0EEF3")
# ── Page setup ─────────────────────────────────────────────────────────────────
PAGE_W, PAGE_H = A4
L_MARGIN = 18 * mm
R_MARGIN = 18 * mm
T_MARGIN = 20 * mm
B_MARGIN = 20 * mm
# ── Styles ─────────────────────────────────────────────────────────────────────
base = getSampleStyleSheet()
def make_style(name, parent="Normal", **kw):
s = ParagraphStyle(name, parent=base[parent])
for k, v in kw.items():
setattr(s, k, v)
return s
sTitle = make_style("sTitle", "Title",
fontName="Helvetica-Bold", fontSize=22,
textColor=C_WHITE, alignment=TA_CENTER,
spaceAfter=2, spaceBefore=0, leading=26)
sSubtitle = make_style("sSubtitle", "Normal",
fontName="Helvetica", fontSize=10,
textColor=C_HEADER_TXT, alignment=TA_CENTER,
spaceAfter=0, leading=14)
sSectionHdr = make_style("sSectionHdr", "Normal",
fontName="Helvetica-Bold", fontSize=11,
textColor=C_WHITE, alignment=TA_LEFT,
spaceBefore=10, spaceAfter=4, leading=16)
sSubHdr = make_style("sSubHdr", "Normal",
fontName="Helvetica-Bold", fontSize=9.5,
textColor=C_NAVY, alignment=TA_LEFT,
spaceBefore=6, spaceAfter=2, leading=13)
sBody = make_style("sBody", "Normal",
fontName="Helvetica", fontSize=8.5,
textColor=C_TEXT, alignment=TA_JUSTIFY,
spaceAfter=4, leading=12)
sBodyBold = make_style("sBodyBold", "Normal",
fontName="Helvetica-Bold", fontSize=8.5,
textColor=C_TEXT, alignment=TA_LEFT,
spaceAfter=2, leading=12)
sBullet = make_style("sBullet", "Normal",
fontName="Helvetica", fontSize=8.2,
textColor=C_TEXT, alignment=TA_LEFT,
leftIndent=10, spaceAfter=2, leading=12)
sTableHdr = make_style("sTableHdr", "Normal",
fontName="Helvetica-Bold", fontSize=8,
textColor=C_WHITE, alignment=TA_CENTER,
leading=11)
sTableCell = make_style("sTableCell", "Normal",
fontName="Helvetica", fontSize=8,
textColor=C_TEXT, alignment=TA_LEFT,
leading=11)
sTableCellC = make_style("sTableCellC", "Normal",
fontName="Helvetica", fontSize=8,
textColor=C_TEXT, alignment=TA_CENTER,
leading=11)
sTableCellB = make_style("sTableCellB", "Normal",
fontName="Helvetica-Bold", fontSize=8,
textColor=C_TEXT, alignment=TA_LEFT,
leading=11)
sAlertRed = make_style("sAlertRed", "Normal",
fontName="Helvetica-Bold", fontSize=8.5,
textColor=C_RED, alignment=TA_LEFT,
spaceAfter=2, leading=12)
sFooter = make_style("sFooter", "Normal",
fontName="Helvetica", fontSize=7,
textColor=colors.HexColor("#7F8C8D"),
alignment=TA_CENTER, leading=10)
sCaption = make_style("sCaption", "Normal",
fontName="Helvetica-Oblique", fontSize=7.5,
textColor=colors.HexColor("#5D6D7E"),
alignment=TA_CENTER, spaceAfter=4, leading=11)
sDrug = make_style("sDrug", "Normal",
fontName="Helvetica", fontSize=8,
textColor=C_TEXT, alignment=TA_LEFT,
spaceAfter=2, leading=12)
# ── Helper flowables ────────────────────────────────────────────────────────────
def HR(color=C_ACCENT, width=1.2):
return HRFlowable(width="100%", thickness=width, color=color, spaceAfter=4, spaceBefore=2)
def section_header(text):
"""Teal background bar with white text used for each major section."""
tbl = Table([[Paragraph(text, sSectionHdr)]], colWidths=[PAGE_W - L_MARGIN - R_MARGIN])
tbl.setStyle(TableStyle([
("BACKGROUND", (0, 0), (-1, -1), C_TEAL),
("LEFTPADDING", (0, 0), (-1, -1), 8),
("RIGHTPADDING", (0, 0), (-1, -1), 8),
("TOPPADDING", (0, 0), (-1, -1), 5),
("BOTTOMPADDING",(0, 0), (-1, -1), 5),
("ROUNDEDCORNERS", [4, 4, 4, 4]),
]))
return tbl
def info_box(text, bg=C_BG_BLUE, border=C_TEAL):
"""Coloured info box."""
tbl = Table([[Paragraph(text, sBody)]], colWidths=[PAGE_W - L_MARGIN - R_MARGIN])
tbl.setStyle(TableStyle([
("BACKGROUND", (0, 0), (-1, -1), bg),
("BOX", (0, 0), (-1, -1), 1, border),
("LEFTPADDING", (0, 0), (-1, -1), 10),
("RIGHTPADDING", (0, 0), (-1, -1), 10),
("TOPPADDING", (0, 0), (-1, -1), 6),
("BOTTOMPADDING",(0, 0), (-1, -1), 6),
]))
return tbl
def alert_box(text, bg=colors.HexColor("#FDEDEC"), border=C_RED):
tbl = Table([[Paragraph(text, sAlertRed)]], colWidths=[PAGE_W - L_MARGIN - R_MARGIN])
tbl.setStyle(TableStyle([
("BACKGROUND", (0, 0), (-1, -1), bg),
("BOX", (0, 0), (-1, -1), 1, border),
("LEFTPADDING", (0, 0), (-1, -1), 10),
("RIGHTPADDING", (0, 0), (-1, -1), 10),
("TOPPADDING", (0, 0), (-1, -1), 6),
("BOTTOMPADDING",(0, 0), (-1, -1), 6),
]))
return tbl
def success_box(text, bg=colors.HexColor("#EAFAF1"), border=C_GREEN):
tbl = Table([[Paragraph(text, sBody)]], colWidths=[PAGE_W - L_MARGIN - R_MARGIN])
tbl.setStyle(TableStyle([
("BACKGROUND", (0, 0), (-1, -1), bg),
("BOX", (0, 0), (-1, -1), 1, border),
("LEFTPADDING", (0, 0), (-1, -1), 10),
("RIGHTPADDING", (0, 0), (-1, -1), 10),
("TOPPADDING", (0, 0), (-1, -1), 6),
("BOTTOMPADDING",(0, 0), (-1, -1), 6),
]))
return tbl
def TC(text, style=None, bold=False):
if style is None:
style = sTableCellB if bold else sTableCell
return Paragraph(text, style)
def TH(text):
return Paragraph(text, sTableHdr)
def build_table(headers, rows, col_widths, alt=True, hdr_color=C_NAVY):
data = [[TH(h) for h in headers]] + [[TC(c) for c in row] for row in rows]
style = [
("BACKGROUND", (0, 0), (-1, 0), hdr_color),
("ROWBACKGROUNDS",(0, 1), (-1, -1), [C_WHITE, C_BG_GREY] if alt else [C_WHITE]),
("GRID", (0, 0), (-1, -1), 0.4, colors.HexColor("#BDC3C7")),
("VALIGN", (0, 0), (-1, -1), "TOP"),
("LEFTPADDING", (0, 0), (-1, -1), 5),
("RIGHTPADDING", (0, 0), (-1, -1), 5),
("TOPPADDING", (0, 0), (-1, -1), 4),
("BOTTOMPADDING", (0, 0), (-1, -1), 4),
]
t = Table(data, colWidths=col_widths)
t.setStyle(TableStyle(style))
return t
# ── Header / Footer callbacks ──────────────────────────────────────────────────
def on_first_page(canvas, doc):
_draw_header(canvas, doc, first=True)
_draw_footer(canvas, doc)
def on_later_pages(canvas, doc):
_draw_header(canvas, doc, first=False)
_draw_footer(canvas, doc)
def _draw_header(canvas, doc, first=False):
canvas.saveState()
if first:
# Full navy title banner
banner_h = 58
canvas.setFillColor(C_NAVY)
canvas.rect(0, PAGE_H - banner_h, PAGE_W, banner_h, fill=1, stroke=0)
# Gold accent stripe
canvas.setFillColor(C_ACCENT)
canvas.rect(0, PAGE_H - banner_h - 3, PAGE_W, 3, fill=1, stroke=0)
else:
# Compact repeat header
hdr_h = 22
canvas.setFillColor(C_NAVY)
canvas.rect(0, PAGE_H - hdr_h, PAGE_W, hdr_h, fill=1, stroke=0)
canvas.setFillColor(C_ACCENT)
canvas.rect(0, PAGE_H - hdr_h - 2, PAGE_W, 2, fill=1, stroke=0)
canvas.setFont("Helvetica-Bold", 9)
canvas.setFillColor(C_WHITE)
canvas.drawString(L_MARGIN, PAGE_H - 15, "TELOGEN EFFLUVIUM — Clinical Reference")
canvas.setFont("Helvetica", 8)
canvas.setFillColor(C_HEADER_TXT)
canvas.drawRightString(PAGE_W - R_MARGIN, PAGE_H - 15, f"Page {doc.page}")
canvas.restoreState()
def _draw_footer(canvas, doc):
canvas.saveState()
canvas.setFillColor(colors.HexColor("#ECF0F1"))
canvas.rect(0, 0, PAGE_W, 16, fill=1, stroke=0)
canvas.setFillColor(C_ACCENT)
canvas.rect(0, 16, PAGE_W, 1, fill=1, stroke=0)
canvas.setFont("Helvetica", 6.5)
canvas.setFillColor(colors.HexColor("#7F8C8D"))
canvas.drawString(L_MARGIN, 5, "Sources: Fitzpatrick's Dermatology (9780071837781) | Andrews' Diseases of the Skin (9780323547536) | Dermatology 2-Volume Set 5e (9780702082252)")
canvas.drawRightString(PAGE_W - R_MARGIN, 5, "For clinical use — verify with current guidelines")
canvas.restoreState()
# ── Build document ─────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
leftMargin=L_MARGIN,
rightMargin=R_MARGIN,
topMargin=T_MARGIN + 45, # extra top for banner
bottomMargin=B_MARGIN + 16,
title="Telogen Effluvium — Clinical Reference",
author="Orris Medical AI",
subject="Dermatology — Hair Loss",
)
story = []
SP = Spacer(1, 5)
SP8 = Spacer(1, 8)
# ═══════════════════════════════════════════════════════
# TITLE SECTION (drawn in canvas; here we just add a spacer)
# ═══════════════════════════════════════════════════════
# We draw the title text directly in the first-page callback
# via a custom Flowable so it sits inside the banner
class TitleFlowable(Flowable):
def __init__(self):
Flowable.__init__(self)
self.width = PAGE_W - L_MARGIN - R_MARGIN
self.height = 0 # zero height — rendered above in canvas callback
def draw(self):
pass # actual drawing done in on_first_page
story.append(TitleFlowable())
# We add explicit title paragraphs as the very first story items,
# but place them INSIDE a table so they render nicely in the banner space.
# Actually simpler: just push them to a spacer + paragraphs aligned to top margin.
# The on_first_page callback draws the navy banner behind them.
# Because reportlab draws canvas decorations behind story items, we push
# the title text using normal paragraphs but with navy-background table so
# they visually merge with the canvas banner.
title_block = Table([
[Paragraph("TELOGEN EFFLUVIUM", sTitle)],
[Paragraph("Clinical Reference Guide | Diagnosis & Management", sSubtitle)],
[Paragraph("Dermatology · August 2026", sSubtitle)],
], colWidths=[PAGE_W - L_MARGIN - R_MARGIN])
title_block.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,-1), C_NAVY),
("TOPPADDING", (0,0),(-1,-1), 4),
("BOTTOMPADDING", (0,0),(-1,-1), 4),
("LEFTPADDING", (0,0),(-1,-1), 0),
("RIGHTPADDING", (0,0),(-1,-1), 0),
]))
story.append(title_block)
story.append(Spacer(1, 10))
# ═══════════════════════════════════════════════════════
# 1 — DEFINITION & OVERVIEW
# ═══════════════════════════════════════════════════════
story.append(section_header("1. DEFINITION & OVERVIEW"))
story.append(SP)
story.append(Paragraph(
"<b>Telogen effluvium (TE)</b> is an acute or chronic diffuse, <b>non-scarring alopecia</b> caused by a premature or "
"excessive shift of hair follicles from the <b>anagen</b> (growth) phase into the <b>telogen</b> (resting/shedding) phase, "
"resulting in abnormal club-hair shedding across the entire scalp.",
sBody))
story.append(SP)
# Key numbers box
story.append(info_box(
"<b>Key numbers:</b> Normal scalp ~ 100,000 follicles | ~10% normally in telogen | "
"Normal daily loss: 100–150 hairs | TE daily loss: 150–400+ hairs"))
story.append(SP8)
# ═══════════════════════════════════════════════════════
# 2 — PATHOPHYSIOLOGY
# ═══════════════════════════════════════════════════════
story.append(section_header("2. PATHOPHYSIOLOGY — Five Mechanisms (Kligman)"))
story.append(SP)
path_data = [
["Mechanism", "Description", "Classic Example"],
["Immediate anagen release", "Abrupt premature conversion of anagen → telogen", "Febrile illness, surgery, childbirth"],
["Delayed anagen release", "Prolonged anagen (e.g. pregnancy), mass release after trigger resolves", "Postpartum TE — shedding 2–3 months after delivery"],
["Short anagen", "Intrinsically shortened anagen; more hairs cycling into telogen constantly", "Chronic TE, androgenetic alopecia"],
["Immediate telogen release","Premature exit from telogen to initiate anagen", "Topical minoxidil (transient early shedding)"],
["Delayed telogen release", "Prolonged telogen; synchronous delayed shedding", "Seasonal shedding"],
]
story.append(build_table(path_data[0], [r for r in path_data[1:]], [85, 200, 190]))
story.append(SP8)
# ═══════════════════════════════════════════════════════
# 3 — ETIOLOGY & TRIGGERS
# ═══════════════════════════════════════════════════════
story.append(section_header("3. ETIOLOGY & COMMON TRIGGERS"))
story.append(SP)
story.append(Paragraph("<b>A. Physiological</b>", sSubHdr))
phys = [
["Postpartum (Telogen Gravidarum)", "Most widely recognised TE. Anagen rate peaks ~94% in pregnancy; telogen rises ~25% by 6 weeks postpartum. Shedding peaks 2–3 months post-delivery."],
["Neonatal TE", "Telogen counts 60–80%; begins within 4 months of birth; resolves spontaneously by 6 months."],
]
story.append(build_table(["Trigger", "Notes"], phys, [130, 345]))
story.append(SP)
story.append(Paragraph("<b>B. Medical / Systemic</b>", sSubHdr))
med = [
["Febrile illness / sepsis", "High fever stresses matrix cells → early anagen release. Post-COVID-19 TE is a current well-recognised example."],
["Major surgery", "Surgery itself or accompanying factors (fever, anaesthesia, hormonal/cytokine changes, nutritional shifts). Rhytidectomy can cause localised TE."],
["Hypothyroidism", "Well-established association. More often causes chronic diffuse telogen hair loss (CDTHL). Hair regrowth begins ~8 weeks after thyroid hormone replacement."],
["Iron deficiency", "Low ferritin / iron saturation. Iron replacement alone may not resolve TE — likely a marker of overall nutritional status."],
["Other", "Renal dialysis with secondary hypervitaminosis A; allergic contact dermatitis to hair dyes; hyperthyroidism (less clear association)."],
]
story.append(build_table(["Trigger", "Notes"], med, [130, 345]))
story.append(SP)
story.append(Paragraph("<b>C. Nutritional</b>", sSubHdr))
nutr = [
["Crash diet / severe calorie restriction", "Weight loss ~12–25 kg over 3 weeks–3 months → telogen counts rise to 25–50%. Classic example shown in Fig 33.4 of Andrews'."],
["Protein deficiency", "Inadequate dietary protein reduces hair matrix cell proliferation."],
["Micronutrient deficiencies", "Iron, zinc, biotin, vitamin D — assess and correct where identified."],
]
story.append(build_table(["Trigger", "Notes"], nutr, [175, 300]))
story.append(SP)
story.append(Paragraph("<b>D. Drug-induced TE</b>", sSubHdr))
story.append(Paragraph(
"Aminosalicylic acid, amphetamines, bromocriptine, captopril, carbamazepine, cimetidine, "
"coumarin, danazol, enalapril, etretinate, levodopa, lithium carbonate, metoprolol, metyrapone, "
"pramipexole, propranolol, pyridostigmine, trimethadione.", sDrug))
story.append(alert_box("Drug-induced TE: Discontinue the offending agent — this is usually curative."))
story.append(SP8)
# ═══════════════════════════════════════════════════════
# 4 — CLINICAL FEATURES
# ═══════════════════════════════════════════════════════
story.append(section_header("4. CLINICAL FEATURES"))
story.append(SP)
clin_data = [
["Feature", "Detail"],
["Pattern", "Diffuse shedding across entire scalp — NOT patchy"],
["Timing", "Typically begins 3–5 months after precipitating event"],
["Shed hair morphology", "Depigmented, club-shaped bulb; no root sheath (telogen club hair)"],
["Scalp appearance", "Normal — no erythema, scaling, or scarring"],
["Bitemporal recession", "Frequently noted, especially in chronic TE"],
["Trichodynia", "Scalp pain/dysaesthesia — common; may coexist with anxiety or depression"],
["Severity", "Patient may notice hair blocking shower drain; clumps of shed hair"],
["Duration", "Acute TE: resolves within 3–6 months. Chronic TE: >6 months, fluctuating"],
]
story.append(build_table(clin_data[0], [r for r in clin_data[1:]], [150, 325]))
story.append(SP8)
# ═══════════════════════════════════════════════════════
# 5 — DIAGNOSIS
# ═══════════════════════════════════════════════════════
story.append(section_header("5. DIAGNOSTIC APPROACH"))
story.append(SP)
story.append(Paragraph("<b>5.1 Clinical Tests</b>", sSubHdr))
diag_data = [
["Test", "Method", "Interpretation"],
["Pull Test",
"Grasp ~40 hairs, apply slow firm traction",
">4–6 club hairs = abnormal (unshampooed scalp)\n>2–3 club hairs = abnormal (freshly shampooed scalp)"],
["Clip Test",
"Cut 25–30 hairs just above scalp surface, mount",
"Short, small-diameter hairs = indeterminate/telogen. Many such hairs in TE or pattern alopecia"],
["Trichogram",
"Pluck 50 hairs with rubber-padded Kelly clamp",
"Provides anagen:telogen ratio. Neonatal TE: telogen ~60–80%"],
["Hair Collection Count",
"Patient collects all shed hairs for 24 h (bed, brush, shower)",
">100 hairs/day sustained over time = significant shedding"],
]
story.append(build_table(diag_data[0], [r for r in diag_data[1:]], [75, 165, 235]))
story.append(SP)
story.append(Paragraph("<b>5.2 Scalp Biopsy (4 mm punch, horizontal/transverse sections)</b>", sSubHdr))
bx_data = [
["Finding", "Significance"],
[">12–15% terminal follicles in telogen", "Significant anagen-to-telogen shift — supports TE diagnosis"],
["Miniaturisation + variable shaft diameter", "Androgenetic alopecia (key differential)"],
["Pigment casts, trichomalacia, catagen hairs", "Traction alopecia / trichotillomania"],
["Normal follicle count in transverse section", "25–50 follicles expected per 4 mm punch"],
]
story.append(build_table(bx_data[0], [r for r in bx_data[1:]], [220, 255]))
story.append(SP)
story.append(Paragraph("<b>5.3 Laboratory Investigations</b>", sSubHdr))
lab_data = [
["Test", "Rationale"],
["Serum ferritin + iron saturation", "Iron deficiency — also a marker of overall nutritional status"],
["TSH ± free T4", "Hypothyroidism (most common thyroid cause); hyperthyroidism less clearly linked"],
["FBC / CBC", "Anaemia, infection markers"],
["Metabolic panel", "Renal function, liver function"],
["Hormonal panel (if indicated)", "DHEAS, testosterone, prolactin — consider in women with signs of hyperandrogenaemia"],
["25-OH Vitamin D, zinc, B12", "Micronutrient deficiencies in prolonged or unexplained TE"],
]
story.append(build_table(lab_data[0], [r for r in lab_data[1:]], [175, 300]))
story.append(SP8)
# ═══════════════════════════════════════════════════════
# 6 — DIFFERENTIAL DIAGNOSIS
# ═══════════════════════════════════════════════════════
story.append(section_header("6. DIFFERENTIAL DIAGNOSIS"))
story.append(SP)
diff_data = [
["Condition", "Key Distinguishing Features", "Biopsy Clue"],
["Androgenetic alopecia", "Patterned (frontoparietal) distribution; miniaturisation", "Miniaturised follicles; variable shaft diameter"],
["Diffuse alopecia areata", "Personal/family hx of AA; nail changes (pitting); body hair loss", "Peribulbar lymphocytic infiltrate ('swarm of bees')"],
["Central centrifugal cicatricial alopecia", "Vertex scarring; women of African descent; progressive", "Scarring; perifollicular fibrosis"],
["Frontal fibrosing alopecia", "Band-like frontotemporal recession; eyebrow loss; perifollicular erythema", "Lymphocytic scarring alopecia"],
["Traction alopecia", "History of tight braiding/tension; frontoparietal margin", "Increased catagen/telogen; no miniaturisation"],
["Tinea capitis", "Children; scaling; broken-off hairs; KOH positive", "Fungal hyphae"],
["Secondary syphilis", "'Moth-eaten' patchy loss; systemic signs; RPR positive", "Perivascular/lichenoid infiltrate"],
]
story.append(build_table(diff_data[0], [r for r in diff_data[1:]], [130, 215, 130]))
story.append(SP8)
# ═══════════════════════════════════════════════════════
# 7 — CHRONIC TELOGEN EFFLUVIUM
# ═══════════════════════════════════════════════════════
story.append(section_header("7. CHRONIC TELOGEN EFFLUVIUM (CTE)"))
story.append(SP)
story.append(info_box(
"<b>Definition:</b> Idiopathic diffuse club-hair loss lasting >6 months with fluctuating course. "
"Described by Whiting in 355 patients (346 women, 9 men), predominantly aged 30–60 years."))
story.append(SP)
cte_data = [
["Feature", "Acute TE", "Chronic TE (CTE)"],
["Duration", "<6 months, self-limiting", ">6 months; fluctuating, may persist years"],
["Trigger", "Identifiable precipitant", "Idiopathic; no clear trigger"],
["Demographics", "Any age, any gender", "Women, 4th–6th decade predominantly"],
["Mechanism", "Premature anagen release", "Shortened anagen phase"],
["Hair density before onset", "Normal", "Often above average — very long hair in childhood"],
["Pull test", "Diffusely positive scalp-wide", "Positive at vertex and occiput"],
["Bitemporal recession", "Less prominent", "Frequently marked"],
["Treatment", "Treat cause; reassure", "5% topical minoxidil; correct modifiable factors"],
["Prognosis", "Excellent — full regrowth", "Variable; may improve with minoxidil"],
]
hdr = cte_data[0]
rows = cte_data[1:]
# Build manually for coloured middle column
data_p = [[TH(h) for h in hdr]]
for r in rows:
data_p.append([TC(r[0], sTableCellB), TC(r[1]), TC(r[2])])
cte_tbl = Table(data_p, colWidths=[130, 185, 160])
cte_tbl.setStyle(TableStyle([
("BACKGROUND", (0, 0), (-1, 0), C_NAVY),
("BACKGROUND", (1, 1), (1, -1), colors.HexColor("#EBF5FB")),
("BACKGROUND", (2, 1), (2, -1), colors.HexColor("#FEF9E7")),
("ROWBACKGROUNDS",(0, 1), (0, -1), [C_WHITE, C_BG_GREY]),
("GRID", (0, 0), (-1, -1), 0.4, colors.HexColor("#BDC3C7")),
("VALIGN", (0, 0), (-1, -1), "TOP"),
("LEFTPADDING", (0, 0), (-1, -1), 5),
("RIGHTPADDING", (0, 0), (-1, -1), 5),
("TOPPADDING", (0, 0), (-1, -1), 4),
("BOTTOMPADDING", (0, 0), (-1, -1), 4),
]))
story.append(cte_tbl)
story.append(SP8)
# ═══════════════════════════════════════════════════════
# 8 — MANAGEMENT
# ═══════════════════════════════════════════════════════
story.append(section_header("8. MANAGEMENT"))
story.append(SP)
story.append(Paragraph("8.1 General Principles", sSubHdr))
story.append(success_box(
"<b>Key principle:</b> No specific therapy is required for most cases of acute TE. "
"The majority resolve spontaneously within 3–6 months once the precipitating trigger resolves. "
"Follicles remain intact — full hair regrowth is expected."))
story.append(SP)
mgmt_data = [
["Scenario", "Management"],
["Drug-induced TE", "Discontinue offending drug — usually curative. Hair regrowth in 3–6 months."],
["Nutritional deficiency", "Correct identified deficiency. Encourage balanced diet. Investigate blood loss (menstrual, GI). Note: iron replacement alone may not resolve TE."],
["Postpartum TE", "Reassurance — self-limiting. Full regrowth expected within 6–12 months post-delivery."],
["Hypothyroidism", "Thyroid hormone replacement. Expect hair regrowth from ~8 weeks post-treatment."],
["Papulosquamous scalp disease", "Treat underlying condition (seborrhoeic dermatitis, psoriasis) — may precipitate TE."],
["Chronic TE (idiopathic)", "5% topical minoxidil solution — may shorten telogen and trigger anagen. Address all modifiable factors."],
["Neonatal TE", "No treatment. Spontaneous resolution by 6 months of age."],
]
story.append(build_table(mgmt_data[0], [r for r in mgmt_data[1:]], [145, 330]))
story.append(SP)
story.append(Paragraph("8.2 Patient Counselling Points", sSubHdr))
counsel = [
"Follicles are intact and will regrow — TE does not cause permanent hair loss.",
"Shedding peaks 3–5 months after the trigger, not immediately after it.",
"Recovery is often slower than expected — allow 6–12 months after trigger resolution.",
"Trichodynia (scalp tenderness) is common and does not indicate disease severity.",
"Avoid unnecessary hair dye products or tight hairstyles during recovery.",
"A balanced, protein-rich diet supports optimal hair re-growth.",
]
for pt in counsel:
story.append(Paragraph(f"• {pt}", sBullet))
story.append(SP8)
# ═══════════════════════════════════════════════════════
# 9 — DIAGNOSTIC ALGORITHM
# ═══════════════════════════════════════════════════════
story.append(section_header("9. DIAGNOSTIC ALGORITHM — Diffuse Hair Loss"))
story.append(SP)
algo_steps = [
("STEP 1", "History",
"Duration of shedding | Precipitating event (illness, surgery, delivery, diet, drugs, stress) | "
"Timing: 3–5 month lag after trigger | Drug history | Nutritional habits | Family history | Menstrual history"),
("STEP 2", "Examination",
"Pull test (>4–6 club hairs = positive) | Scalp inspection (scarring? scaling? erythema?) | "
"Distribution (diffuse vs patterned) | Nail changes | Body hair | Thyroid exam"),
("STEP 3", "Initial Labs",
"Serum ferritin + iron saturation | TSH | FBC | If indicated: hormonal screen, metabolic panel, vitamin D, zinc"),
("STEP 4", "Trichoscopy (if available)",
"Vacant follicular ostia (yellow dots) | Upright regrowing hairs | Diversity of hair shaft diameter"),
("STEP 5", "Scalp Biopsy (if diagnosis unclear)",
"Horizontal sections | >12–15% terminal follicles in telogen confirms TE | Exclude scarring alopecias"),
("STEP 6", "Diagnosis & Management",
"Acute TE with identifiable trigger → treat cause + reassure | Chronic TE (>6 months, idiopathic) → "
"5% minoxidil + correct modifiable factors | Refer dermatology if uncertain or refractory"),
]
for step, title, desc in algo_steps:
row = Table([[
Paragraph(step, make_style(f"algo_{step}", "Normal",
fontName="Helvetica-Bold", fontSize=8, textColor=C_WHITE,
alignment=TA_CENTER, leading=11)),
Paragraph(f"<b>{title}:</b> {desc}", sBody)
]], colWidths=[45, PAGE_W - L_MARGIN - R_MARGIN - 55])
row.setStyle(TableStyle([
("BACKGROUND", (0, 0), (0, 0), C_TEAL),
("BACKGROUND", (1, 0), (1, 0), C_BG_BLUE),
("BOX", (0, 0), (-1, -1), 0.5, C_TEAL),
("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
("LEFTPADDING", (0, 0), (-1, -1), 6),
("RIGHTPADDING", (0, 0), (-1, -1), 6),
("TOPPADDING", (0, 0), (-1, -1), 5),
("BOTTOMPADDING",(0, 0), (-1, -1), 5),
]))
story.append(row)
story.append(Spacer(1, 2))
story.append(SP8)
# ═══════════════════════════════════════════════════════
# 10 — PROGNOSIS
# ═══════════════════════════════════════════════════════
story.append(section_header("10. PROGNOSIS"))
story.append(SP)
story.append(success_box(
"<b>Overall prognosis is excellent when a clear precipitating event is identified.</b> "
"Full hair regrowth is the norm for acute TE. Neonatal TE always resolves by 6 months. "
"Chronic idiopathic TE has a fluctuating course but is non-scarring and responds partially "
"to 5% minoxidil. Female pattern hair loss must be excluded before diagnosing CTE."))
story.append(SP)
prog_data = [
["Type of TE", "Expected Outcome", "Timeframe"],
["Postpartum", "Full regrowth", "6–12 months post-delivery"],
["Post-febrile illness", "Full regrowth", "3–6 months after recovery"],
["Drug-induced", "Full regrowth", "3–6 months after stopping drug"],
["Post-surgical", "Full regrowth", "3–6 months post-operatively"],
["Nutritional/dietary", "Full regrowth", "After nutritional correction — 3–6 months"],
["Hypothyroid", "Full regrowth", "Begins ~8 weeks after treatment"],
["Neonatal", "Full regrowth — always", "By 6 months of age"],
["Chronic TE (idiopathic)", "Partial/variable", "Fluctuating; may require long-term minoxidil"],
]
story.append(build_table(prog_data[0], [r for r in prog_data[1:]], [145, 200, 130]))
story.append(SP8)
# ═══════════════════════════════════════════════════════
# 11 — QUICK REFERENCE CARD
# ═══════════════════════════════════════════════════════
story.append(section_header("11. QUICK REFERENCE — At a Glance"))
story.append(SP)
qr_left = [
("<b>Diagnose TE when:</b>", None),
("• Diffuse shedding, 3–5 months after trigger", sBody),
("• Pull test >4–6 club hairs positive", sBody),
("• Club-shaped, non-pigmented bulb on shed hair", sBody),
("• Normal scalp — no scarring, no erythema", sBody),
("• Ferritin, TSH normal (or abnormal — explains TE)", sBody),
("", None),
("<b>Exclude before diagnosing TE:</b>", None),
("• Androgenetic alopecia (patterned, miniaturisation)", sBody),
("• Alopecia areata (patches, nail changes)", sBody),
("• Scarring alopecia (permanent loss risk)", sBody),
("• Tinea capitis in children", sBody),
]
qr_right = [
("<b>Immediate action:</b>", None),
("• Identify and remove/treat the trigger", sBody),
("• Check ferritin + TSH in all cases", sBody),
("• Reassure — follicles intact, hair WILL regrow", sBody),
("", None),
("<b>Refer to Dermatology if:</b>", None),
("• Diagnosis unclear after workup", sBody),
("• Shedding >12 months without resolution", sBody),
("• Scalp biopsy required", sBody),
("• Suspected scarring alopecia", sBody),
("• Minoxidil not tolerated or ineffective", sBody),
]
def qr_paras(items):
out = []
for text, style in items:
if style is None:
out.append(Paragraph(text, sSubHdr if text else sBody))
else:
out.append(Paragraph(text, style))
return out
qr_tbl = Table([[qr_paras(qr_left), qr_paras(qr_right)]],
colWidths=[(PAGE_W - L_MARGIN - R_MARGIN) / 2 - 4,
(PAGE_W - L_MARGIN - R_MARGIN) / 2 - 4])
qr_tbl.setStyle(TableStyle([
("BACKGROUND", (0, 0), (0, 0), colors.HexColor("#EBF4F8")),
("BACKGROUND", (1, 0), (1, 0), colors.HexColor("#EAFAF1")),
("BOX", (0, 0), (0, 0), 0.6, C_TEAL),
("BOX", (1, 0), (1, 0), 0.6, C_GREEN),
("VALIGN", (0, 0), (-1, -1), "TOP"),
("LEFTPADDING", (0, 0), (-1, -1), 10),
("RIGHTPADDING", (0, 0), (-1, -1), 10),
("TOPPADDING", (0, 0), (-1, -1), 8),
("BOTTOMPADDING",(0, 0), (-1, -1), 8),
]))
story.append(qr_tbl)
story.append(SP8)
story.append(HR())
story.append(Paragraph(
"Sources: Fitzpatrick's Dermatology (9780071837781) | Andrews' Diseases of the Skin (9780323547536) | "
"Dermatology 2-Volume Set 5e (9780702082252) | Compiled by Orris Medical AI, August 2026",
sFooter))
# ── Build ──────────────────────────────────────────────────────────────────────
doc.build(story, onFirstPage=on_first_page, onLaterPages=on_later_pages)
print(f"PDF written to: {OUTPUT}")
cd /tmp/workspace/telogen-effluvium && python generate_pdf.py 2>&1
ls -lh /tmp/workspace/telogen-effluvium/Telogen_Effluvium_Clinical_Reference.pdf
Telogen Effluvium Clinical Reference
PDF Document · PDF
| Section | Content |
|---|---|
| 1. Definition & Overview | Core definition, key numbers (follicle count, normal vs TE shedding rates) |
| 2. Pathophysiology | Kligman's 5 mechanisms with examples (colour-coded table) |
| 3. Etiology & Triggers | Physiological, medical/systemic, nutritional, drug-induced (full drug list) |
| 4. Clinical Features | Pattern, timing, shed hair morphology, trichodynia |
| 5. Diagnostic Approach | Pull test, clip test, trichogram, biopsy criteria, full lab panel |
| 6. Differential Diagnosis | 7 conditions with distinguishing features and biopsy clues |
| 7. Chronic TE (CTE) | Acute vs chronic comparison table |
| 8. Management | Per-scenario management table + 6-point patient counselling guide |
| 9. Diagnostic Algorithm | 6-step step-by-step clinical workflow |
| 10. Prognosis | Expected outcomes and timeframes by TE type |
| 11. Quick Reference Card | Two-column at-a-glance summary (diagnose when / immediate action) |