---
format: typebulb/v1
name: "Bayes' Calculator"
---

**code.tsx**

```tsx
import { App, Component, getPropertyValue, formField, inputRange, div, h1, span, strong, type HValues } from "domeleon";

class BayesCalculator extends Component {
  prior = 0.1
  sensitivity = 0.9
  falsePositiveRate = 0.05

  get marginalLikelihood() {
    const truePositives = this.sensitivity * this.prior
    return truePositives + this.falsePositiveRate * (1 - this.prior)
  }

  get posterior() {
    const numerator = this.sensitivity * this.prior
    const denominator = this.marginalLikelihood
    return denominator === 0 ? 0 : numerator / denominator
  }

  slider (prop: () => number, labelText: string, notation: string, paramClass: string) {
    return formField({
      target: this,
      prop,      
      inputFn: inputRange,
      fieldAttrs: { class: "control-row" },
      inputProps: { attrs: { min: "0", max: "1", step: "0.01" } },
      label: div(
        `${labelText} (`,
        span({ class: paramClass }, notation),
        "): ",
        strong({ class: paramClass }, getPropertyValue(this, prop).toFixed(2))
      )
    })  
  }

  private controls() {
    return div({ class: "controls" },
      this.slider(() => this.prior, "Prior Probability", "P(A)", "param-prior"),
      this.slider(() => this.sensitivity, "Sensitivity", "P(B|A)", "param-sensitivity"),
      this.slider(() => this.falsePositiveRate, "False Positive Rate", "P(B|¬A)", "param-fpr")
    )
  }

  // Helper to create a region with subregions
  private region(leftPercent: number, widthPercent: number, isA: boolean) {
    const truePositiveHeightPercent = (isA ? this.sensitivity : this.falsePositiveRate) * 100
    const complementHeightPercent = 100 - truePositiveHeightPercent
    const regionClass = isA ? "region-a" : "region-nota"
    const positiveClass = isA ? "subregion-ab" : "subregion-notab"
    const negativeClass = isA ? "subregion-anotb" : "subregion-notanotb"

    return div({ 
      class: `region ${regionClass}`,
      style: { left: `${leftPercent}%`, width: `${widthPercent}%` }
    },
      div({ class: `subregion ${positiveClass}`, style: { height: `${truePositiveHeightPercent}%` } }),
      div({ class: `subregion ${negativeClass}`, style: { height: `${complementHeightPercent}%` } })
    );
  }

  private visualization() {
    const priorPercent = this.prior * 100
    const notAPercent = (1 - this.prior) * 100

    return div({ class: "bayes-rect" },
      this.region(0, priorPercent, true),
      this.region(priorPercent, notAPercent, false)
    );
  }

  // Helper to create a fraction in an equation step
  private fraction(numeratorContent: HValues[], denominatorContent: HValues[]) {
    return div({ class: "fraction" },
      div({ class: "numerator" }, ...numeratorContent),
      div({ class: "denominator" }, ...denominatorContent)
    );
  }

  // The famous Bayes' theorem formula: P(A|B) = P(B|A) × P(A) / P(B)
  private bayesFormula() {
    return this.fraction(
      [
        span({ class: "param-sensitivity" }, "P(B|A)"),
        " × ",
        span({ class: "param-prior" }, "P(A)")
      ],
      [
        span({ class: "param-marginal" }, "P(B)")
      ]
    )
  }

  // Helper to create the full Bayes' theorem expansion (symbolic or numerical)
  private bayesExpansion(useNumbers: boolean) {
    const mult = " × "
    const sens = useNumbers ? this.sensitivity.toFixed(2) : "P(B|A)"
    const pri = useNumbers ? this.prior.toFixed(2) : "P(A)"
    const fpr = useNumbers ? this.falsePositiveRate.toFixed(2) : "P(B|¬A)"
    const notA = useNumbers ? (1 - this.prior).toFixed(2) : "P(¬A)"

    const sensClass = "param-sensitivity"
    const priClass = "param-prior"
    const fprClass = "param-fpr"
    const notAClass = "param-nota"

    return this.fraction(
      [
        span({ class: sensClass }, sens),
        mult,
        span({ class: priClass }, pri)
      ],
      [
        span({ class: sensClass }, sens),
        mult,
        span({ class: priClass }, pri),
        " + ",
        span({ class: fprClass }, fpr),
        mult,
        span({ class: notAClass }, notA)
      ]
    )
  }

  private equations() {
    return div({ class: "equation-row" },
      div({ class: "eq-lhs-container" },
        div({ class: "eq-lhs" },
          span({ class: "param-posterior" }, "P(A|B)"),
          span({ class: "equals-inline" }, "=")
        ),
        div({ class: "eq-label" }, "(Posterior)")
      ),
      div({ class: "eq-rhs-stack" },
        div({ class: "eq-step" }, this.bayesFormula()),
        div({ class: "eq-step" }, this.bayesExpansion(false)),
        div({ class: "eq-step" }, this.bayesExpansion(true)),
        div({ class: "eq-step eq-result" },
          div({ class: "result-value param-posterior" }, this.posterior.toFixed(3))
        )
      )
    );
  }

  view() {
    return div({ class: "app" },
      h1("Bayes' Calculator"),
      this.controls(),
      this.visualization(),
      this.equations()
    )
  }
}

new App({ root: new BayesCalculator(), id: "app" })
```
**styles.css**

```css
/* Theme: Light Mode */
html[data-theme="light"] {
  color-scheme: light;
  --bg-primary: #ffffff;
  --text-primary: #333333;
  --bg-container: #fafafa;
  --border-container: #e5e5e5;
  --border-color: #333333;

  --color-prior: rgb(0, 69, 229);     /* P(A) - also A∩¬B region */
  --color-nota: rgb(79, 149, 255);    /* P(¬A) - right side total */
  --color-sensitivity: #059669;       /* P(B|A) - also A∩B region */
  --color-fpr: #dc2626;               /* P(B|¬A) - also ¬A∩B region */
  --color-marginal: #d97706;          /* P(B) - marginal likelihood */
  --color-posterior: #9333ea;         /* P(A|B) */

  --color-truenegative: #94a3b8;      /* True negative: ¬A∩¬B */
  --region-a-bg: #dbeafe;             /* Light background for A region */
  --region-nota-bg: #e6e6e6;          /* Light background for ¬A region */
}

/* Theme: Dark Mode */
html[data-theme="dark"] {
  color-scheme: dark;
  --bg-primary: #1e1e1e;
  --text-primary: #d4d4d4;
  --bg-container: #252525;
  --border-container: #3a3a3a;
  --border-color: #666666;

  --color-prior: #3b82f6;             /* P(A) - also A∩¬B region - deeper blue for contrast with green */
  --color-nota: #93c5fd;              /* P(¬A) - right side total - lighter blue for contrast with bg */
  --color-sensitivity: #10b981;       /* P(B|A) - also A∩B region */
  --color-fpr: #ef4444;               /* P(B|¬A) - also ¬A∩B region */
  --color-marginal: #f59e0b;          /* P(B) - marginal likelihood */
  --color-posterior: #a855f7;         /* P(A|B) */

  --color-truenegative: #475569;      /* True negative: ¬A∩¬B */
  --region-a-bg: #1a3a52;             /* Dark background for A region */
  --region-nota-bg: #2a2a2a;          /* Dark background for ¬A region */
}

/* Base Styles */
body {
  font-family: sans-serif;
  margin: 0;
  padding: 1rem 0.5rem;
  background: var(--bg-primary);
  color: var(--text-primary);
  min-height: 100vh;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow-x: hidden;
}

.app {
  max-width: 900px;
  width: 100%;
  margin: 0 auto;
  padding: 2rem;
  background: var(--bg-container);
  border: 1px solid var(--border-container);
  border-radius: 8px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
  box-sizing: border-box;
}



h1 {
  text-align: center;
  margin: 0.5em 0 1em 0;
}

/* Bayesian Visualization Rectangle */
.region { position: absolute; top: 0; height: 100%; }
.region-a { background: var(--region-a-bg); }
.region-nota { background: var(--region-nota-bg); }

.bayes-rect {
  position: relative;
  width: 300px;
  height: 200px;
  margin: 3em auto 0;
}

.subregion-ab { background: var(--color-sensitivity); }
.subregion-anotb { background: var(--color-prior); }
.subregion-notab { background: var(--color-fpr); }
.subregion-notanotb { background: var(--color-truenegative); }

/* Parameter Controls */
.controls {
  margin: 0 0 3em 0;
}
.control-row { margin-bottom: 1.5em; }

.control-row > div {
  display: flex;
  flex-direction: column;
  gap: 0.5em;
}

.control-row label { text-align: right; }
input[type='range'] {
  width: 100%;
}

@media (min-width: 640px) {
  .control-row > div {
    display: grid;
    grid-template-columns: 300px 1fr;
    gap: 1.5em;
    align-items: center;
  }
}

/* Equation Display */
.equation-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1em;
  margin: 1em 0 0 0;
  padding: 1em;
}

.eq-lhs-container {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.25em;
}

.eq-label {
  font-size: 1.1em;
  font-style: normal;
  font-family: sans-serif;
  color: var(--text-primary);
  opacity: 0.7;
}

.eq-lhs {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 0.2em;
  font-size: 1.1em;
  font-family: 'Georgia', 'Cambria Math', serif;
  font-style: italic;
}

.eq-rhs-stack { display: flex; flex-direction: column; gap: 1em; }

.eq-step {
  font-size: 1.1em;
  font-family: 'Georgia', 'Cambria Math', serif;
  font-style: italic;
  display: flex;
  justify-content: center;
  align-items: center;
}

.eq-result { font-style: normal; }

.result-value {
  font-weight: bold;
  font-size: 1.8em;
  font-family: 'Consolas', 'Monaco', monospace;
}

.equals-inline {
  font-size: 1.3em;
  font-weight: bold;
  color: var(--text-primary);
  font-style: normal;
  margin: 0 0.25em;
}

.fraction {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  vertical-align: middle;
}

.numerator { padding: 0.25em 0.5em; border-bottom: 2px solid var(--text-primary); text-align: center; }
.denominator { padding: 0.25em 0.5em; text-align: center; }

/* Parameter Color Coding */
[class^="param-"] { font-weight: 600; }

.param-prior { color: var(--color-prior); }
.param-nota { color: var(--color-nota); }
.param-sensitivity { color: var(--color-sensitivity); }
.param-fpr { color: var(--color-fpr); }
.param-marginal { color: var(--color-marginal); }
.param-posterior { color: var(--color-posterior); }
```
**index.html**

```html
<div id="app"></div>
```
**config.json**

```json
{
  "dependencies": {
    "domeleon": "^0.5.2"
  },
  "description": "A very visual Bayesian calculator."
}
```