edgarodriguez commited on
Commit
2f22a9f
·
verified ·
1 Parent(s): 1c5fed2

Upload 2 files

Browse files
Files changed (2) hide show
  1. depth_interactive_map_alpha.R +51 -21
  2. styles.css +30 -28
depth_interactive_map_alpha.R CHANGED
@@ -54,6 +54,29 @@ source("helpers.R")
54
 
55
  `%||%` <- function(x, y) if (is.null(x) || length(x) == 0) y else x
56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  # Deployment edit (v10_HF): DuckDB path from env var, baked into the image.
58
  DB_PATH <- Sys.getenv("DEPTH_DDB_PATH", "/app/depth_mexico.duckdb")
59
 
@@ -1297,14 +1320,14 @@ WEB_SPEC <- list(
1297
  subtitle = "Summary of main indicators for migrants across Mexico.",
1298
  groups = list(
1299
  list(id = "hazards", label = "Hazards", open = TRUE, cards = list(
1300
- list(title = "Missing Migrants Incidents (IOM)", kind = "stat",
1301
  stats = list(
1302
  list(id = "n_incidents", label = "Recorded incidents", color = COL_YEL),
1303
  list(id = "n_fatalities", label = "Confirmed dead", color = COL_RED)
1304
  ),
1305
  charts = list(
1306
  list(id = "migrants_treemap_cause", label = "Cause of death",
1307
- height = "70px"),
1308
  list(id = "migrants_treemap_demo", label = "Demographics (children / female / male)",
1309
  height = "44px")
1310
  )),
@@ -1670,10 +1693,12 @@ LUCIDE_JS <- tags$head(
1670
  };
1671
 
1672
  // Format a ym integer back to year-dash-month (e.g. 2014-03) for the label.
 
 
1673
  function _ymFmt(v) {
1674
  var y = Math.floor(v / 12);
1675
- var m = (v % 12) + 1;
1676
- return y + '-' + (m < 10 ? '0' + m : '' + m);
1677
  }
1678
 
1679
  function syncTemporalStrip(checkedIds) {
@@ -1791,7 +1816,7 @@ LUCIDE_JS <- tags$head(
1791
  if (document.activeElement === startEl) { hi = lo; endEl.value = hi; }
1792
  else { lo = hi; startEl.value = lo; }
1793
  }
1794
- if (label) label.textContent = _ymFmt(lo) + ' -- ' + _ymFmt(hi);
1795
  if (fill) {
1796
  var min = parseInt(startEl.min, 10);
1797
  var max = parseInt(startEl.max, 10);
@@ -2706,7 +2731,8 @@ render_panel_groups <- function() {
2706
 
2707
  ui <- page_fillable(
2708
  theme = bs_theme(version = 5, bg = "#FAFAFB", fg = "#0F172A",
2709
- primary = "#006B82", base_font = font_google("Inter")),
 
2710
  padding = 0,
2711
  LUCIDE_JS,
2712
  includeCSS("styles.css"),
@@ -4587,14 +4613,18 @@ server <- function(input, output, session) {
4587
  # to the in-polygon subset when one is). Treemap geometry is a 2-row slice-
4588
  # and-dice -- the dominant category gets the top row, smaller categories
4589
  # tile the bottom row. Avoids the treemapify dependency.
4590
- CAUSE_PAL <- c("Other / unknown" = "#6b7280",
4591
- "Drowning" = "#0ea5e9",
4592
- "Harsh environment" = "#f59e0b",
4593
- "Vehicle accident" = "#a855f7",
4594
- "Violence" = "#ef4444")
4595
- DEMO_PAL <- c(Children = "#f97316",
4596
- Female = "#ec4899",
4597
- Male = "#0987A8")
 
 
 
 
4598
 
4599
  treemap_layout <- function(df) {
4600
  # df: data.frame(label, value, color); returns df with xmin/xmax/ymin/ymax
@@ -4642,12 +4672,12 @@ server <- function(input, output, session) {
4642
  ggplot(laid, aes(xmin = xmin, xmax = xmax,
4643
  ymin = ymin, ymax = ymax,
4644
  fill = label)) +
4645
- geom_rect(color = "#0f172a", linewidth = 0.4) +
4646
  geom_text(aes(x = cx, y = cy + 0.10, label = short),
4647
- size = 2.2, color = "white",
4648
  fontface = "bold", lineheight = 0.9) +
4649
  geom_text(aes(x = cx, y = cy - 0.18, label = paste0(pct, "%")),
4650
- size = 1.9, color = "white", alpha = 0.85) +
4651
  scale_fill_manual(values = pal, na.value = "#334155") +
4652
  coord_cartesian(expand = FALSE, xlim = c(0, 1), ylim = c(0, 1)) +
4653
  theme_void() +
@@ -4727,7 +4757,7 @@ server <- function(input, output, session) {
4727
  geom_point(data = df_sel, aes(x = yr, y = n),
4728
  color = line_color, size = 1.6, shape = 21,
4729
  fill = "white", stroke = 0.7)
4730
- p + theme_minimal(base_size = 8) +
4731
  theme(
4732
  plot.background = element_rect(fill = "transparent", color = NA),
4733
  panel.background = element_rect(fill = "transparent", color = NA),
@@ -4782,19 +4812,19 @@ server <- function(input, output, session) {
4782
 
4783
  ggplot(df_all, aes(x = ym, y = n, fill = in_win)) +
4784
  geom_col(width = 1, color = NA) +
4785
- scale_fill_manual(values = c(`FALSE` = "#ffffff22",
4786
  `TRUE` = bar_color)) +
4787
  scale_x_continuous(breaks = yr_breaks, labels = yr_labels,
4788
  limits = c(ym_data_lo - 0.5, ym_data_hi + 0.5),
4789
  expand = expansion(mult = c(0, 0))) +
4790
  scale_y_continuous(expand = expansion(mult = c(0, 0.05))) +
4791
  labs(x = NULL, y = NULL) +
4792
- theme_minimal(base_size = 8) +
4793
  theme(plot.background = element_rect(fill = "transparent", color = NA),
4794
  panel.background = element_rect(fill = "transparent", color = NA),
4795
  panel.grid = element_blank(),
4796
  axis.title = element_blank(),
4797
- axis.text.x = element_text(size = 7.5, color = "#94a3b8"),
4798
  axis.text.y = element_blank(),
4799
  axis.ticks = element_blank(),
4800
  legend.position = "none",
 
54
 
55
  `%||%` <- function(x, y) if (is.null(x) || length(x) == 0) y else x
56
 
57
+ # --- Plot fonts -----------------------------------------------------------------
58
+ # ggplot text (treemaps, temporal strip, mini-charts) uses the Rights Lab brand
59
+ # font when showtext + sysfonts are available in the image; otherwise it falls
60
+ # back to the device sans (never Inter). Guarded so a missing package or an
61
+ # offline Google fetch can never break the app at startup.
62
+ PLOT_FONT <- "sans"
63
+ if (requireNamespace("showtext", quietly = TRUE) &&
64
+ requireNamespace("sysfonts", quietly = TRUE)) {
65
+ font_ok <- tryCatch({
66
+ sysfonts::font_add_google("Source Sans 3", "depth_sans")
67
+ TRUE
68
+ }, error = function(e) {
69
+ message("[fonts] Google font fetch failed; using system sans: ",
70
+ conditionMessage(e))
71
+ FALSE
72
+ })
73
+ if (isTRUE(font_ok)) {
74
+ PLOT_FONT <- "depth_sans"
75
+ showtext::showtext_auto()
76
+ showtext::showtext_opts(dpi = 110)
77
+ }
78
+ }
79
+
80
  # Deployment edit (v10_HF): DuckDB path from env var, baked into the image.
81
  DB_PATH <- Sys.getenv("DEPTH_DDB_PATH", "/app/depth_mexico.duckdb")
82
 
 
1320
  subtitle = "Summary of main indicators for migrants across Mexico.",
1321
  groups = list(
1322
  list(id = "hazards", label = "Hazards", open = TRUE, cards = list(
1323
+ list(title = "Deaths during migration", kind = "stat",
1324
  stats = list(
1325
  list(id = "n_incidents", label = "Recorded incidents", color = COL_YEL),
1326
  list(id = "n_fatalities", label = "Confirmed dead", color = COL_RED)
1327
  ),
1328
  charts = list(
1329
  list(id = "migrants_treemap_cause", label = "Cause of death",
1330
+ height = "60px"),
1331
  list(id = "migrants_treemap_demo", label = "Demographics (children / female / male)",
1332
  height = "44px")
1333
  )),
 
1693
  };
1694
 
1695
  // Format a ym integer back to year-dash-month (e.g. 2014-03) for the label.
1696
+ var _MONTHS = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug',
1697
+ 'Sep','Oct','Nov','Dec'];
1698
  function _ymFmt(v) {
1699
  var y = Math.floor(v / 12);
1700
+ var m = v % 12; // 0 = January (ym packs month - 1)
1701
+ return _MONTHS[m] + ' ' + y;
1702
  }
1703
 
1704
  function syncTemporalStrip(checkedIds) {
 
1816
  if (document.activeElement === startEl) { hi = lo; endEl.value = hi; }
1817
  else { lo = hi; startEl.value = lo; }
1818
  }
1819
+ if (label) label.textContent = _ymFmt(lo) + ' to ' + _ymFmt(hi);
1820
  if (fill) {
1821
  var min = parseInt(startEl.min, 10);
1822
  var max = parseInt(startEl.max, 10);
 
2731
 
2732
  ui <- page_fillable(
2733
  theme = bs_theme(version = 5, bg = "#FAFAFB", fg = "#0F172A",
2734
+ primary = "#006B82",
2735
+ base_font = font_google("Source Sans 3")),
2736
  padding = 0,
2737
  LUCIDE_JS,
2738
  includeCSS("styles.css"),
 
4613
  # to the in-polygon subset when one is). Treemap geometry is a 2-row slice-
4614
  # and-dice -- the dominant category gets the top row, smaller categories
4615
  # tile the bottom row. Avoids the treemapify dependency.
4616
+ # Re-toned to the muted Spatial data palette so the treemaps speak the same
4617
+ # colour language as the map points (no Tailwind-bright sky / red / purple).
4618
+ CAUSE_PAL <- c("Other / unknown" = "#374151", # neutral slate (COL_COUNT)
4619
+ "Drowning" = "#0987A8", # teal-blue / water
4620
+ "Harsh environment" = "#B89307", # ochre / heat
4621
+ "Vehicle accident" = "#7E4A86", # mauve
4622
+ "Violence" = "#9E2A3A") # brick red
4623
+ # Demographics are nominal (not ordered) and carry no risk valence -- three
4624
+ # distinct muted hues, avoiding the pink = female / blue = male cliche.
4625
+ DEMO_PAL <- c(Children = "#B89307", # ochre
4626
+ Female = "#7E4A86", # mauve
4627
+ Male = "#3A4B5C") # slate
4628
 
4629
  treemap_layout <- function(df) {
4630
  # df: data.frame(label, value, color); returns df with xmin/xmax/ymin/ymax
 
4672
  ggplot(laid, aes(xmin = xmin, xmax = xmax,
4673
  ymin = ymin, ymax = ymax,
4674
  fill = label)) +
4675
+ geom_rect(color = "#ffffff", linewidth = 0.7) +
4676
  geom_text(aes(x = cx, y = cy + 0.10, label = short),
4677
+ size = 2.2, color = "white", family = PLOT_FONT,
4678
  fontface = "bold", lineheight = 0.9) +
4679
  geom_text(aes(x = cx, y = cy - 0.18, label = paste0(pct, "%")),
4680
+ size = 1.9, color = "white", family = PLOT_FONT, alpha = 0.85) +
4681
  scale_fill_manual(values = pal, na.value = "#334155") +
4682
  coord_cartesian(expand = FALSE, xlim = c(0, 1), ylim = c(0, 1)) +
4683
  theme_void() +
 
4757
  geom_point(data = df_sel, aes(x = yr, y = n),
4758
  color = line_color, size = 1.6, shape = 21,
4759
  fill = "white", stroke = 0.7)
4760
+ p + theme_minimal(base_size = 8, base_family = PLOT_FONT) +
4761
  theme(
4762
  plot.background = element_rect(fill = "transparent", color = NA),
4763
  panel.background = element_rect(fill = "transparent", color = NA),
 
4812
 
4813
  ggplot(df_all, aes(x = ym, y = n, fill = in_win)) +
4814
  geom_col(width = 1, color = NA) +
4815
+ scale_fill_manual(values = c(`FALSE` = "#cdd3db",
4816
  `TRUE` = bar_color)) +
4817
  scale_x_continuous(breaks = yr_breaks, labels = yr_labels,
4818
  limits = c(ym_data_lo - 0.5, ym_data_hi + 0.5),
4819
  expand = expansion(mult = c(0, 0))) +
4820
  scale_y_continuous(expand = expansion(mult = c(0, 0.05))) +
4821
  labs(x = NULL, y = NULL) +
4822
+ theme_minimal(base_size = 8, base_family = PLOT_FONT) +
4823
  theme(plot.background = element_rect(fill = "transparent", color = NA),
4824
  panel.background = element_rect(fill = "transparent", color = NA),
4825
  panel.grid = element_blank(),
4826
  axis.title = element_blank(),
4827
+ axis.text.x = element_text(size = 7.5, color = "#475569"),
4828
  axis.text.y = element_blank(),
4829
  axis.ticks = element_blank(),
4830
  legend.position = "none",
styles.css CHANGED
@@ -3,7 +3,7 @@
3
  Renamed from 10.08_styles.css on 2026-05-20.
4
  ============================================================================ */
5
 
6
- @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&family=JetBrains+Mono:wght@500;600&display=swap');
7
 
8
  /* -- Design tokens --------------------------------------------------------- */
9
  :root {
@@ -61,17 +61,18 @@
61
  /* Glass blur */
62
  --blur-glass: saturate(140%) blur(14px);
63
 
64
- /* Typography — Inter throughout, mono only for stats / coordinates */
65
- --font-ui: 'Inter', system-ui, -apple-system, sans-serif;
66
- --font-mono: 'JetBrains Mono', 'SF Mono', ui-monospace, monospace;
 
67
 
68
- /* Back-compat aliases — keep legacy class refs working */
69
- --font-label: var(--font-ui);
70
  --font-body: var(--font-ui);
71
 
72
  /* Motion — Spatial spring */
73
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
74
- --ease-spring: cubic-bezier(0.34, 1.4, 0.5, 1);
75
  --t-fast: 140ms; --t-base: 220ms; --t-slow: 320ms;
76
 
77
  /* Radii */
@@ -421,9 +422,9 @@
421
  }
422
 
423
  .stat-val {
424
- font-family: var(--font-mono);
425
  font-size: 30px;
426
- font-weight: 600;
427
  font-variant-numeric: tabular-nums;
428
  line-height: 1;
429
  margin-bottom: 4px;
@@ -628,7 +629,6 @@
628
  }
629
 
630
  .ind-card:hover {
631
- transform: translateY(-1px);
632
  box-shadow: var(--depth-2);
633
  }
634
 
@@ -1358,9 +1358,10 @@ button.mapbox-gl-draw_uncombine { display: none !important; }
1358
 
1359
  /* ---- Brand ---- */
1360
  .nav-brand {
1361
- font-size: 20px;
1362
- font-weight: 900;
1363
- letter-spacing: 0.16em;
 
1364
  text-transform: uppercase;
1365
  color: var(--c-text);
1366
  padding: 0 var(--space-2);
@@ -2512,10 +2513,10 @@ body.tooltips-off .maplibregl-popup { display: none !important; }
2512
  margin-left: auto;
2513
  margin-right: auto;
2514
  z-index: 950;
2515
- background: oklch(15% 0.012 250 / 0.92);
2516
- backdrop-filter: blur(12px) saturate(140%);
2517
- -webkit-backdrop-filter: blur(12px) saturate(140%);
2518
- border: 1px solid oklch(100% 0 0 / 0.12);
2519
  border-radius: var(--radius-md);
2520
  box-shadow: var(--depth-3);
2521
  padding: 6px 12px 8px;
@@ -2558,8 +2559,8 @@ body.tooltips-off .maplibregl-popup { display: none !important; }
2558
  .temporal-play-btn,
2559
  .temporal-reset-btn {
2560
  background: transparent;
2561
- color: oklch(95% 0 0);
2562
- border: 1px solid oklch(100% 0 0 / 0.18);
2563
  cursor: pointer;
2564
  font-family: var(--font-ui);
2565
  transition: background var(--t-fast) var(--ease-out),
@@ -2586,8 +2587,9 @@ body.tooltips-off .maplibregl-popup { display: none !important; }
2586
  }
2587
  .temporal-play-btn:hover,
2588
  .temporal-reset-btn:hover {
2589
- background: oklch(100% 0 0 / 0.10);
2590
- border-color: oklch(100% 0 0 / 0.3);
 
2591
  }
2592
  .temporal-play-btn:focus-visible,
2593
  .temporal-reset-btn:focus-visible {
@@ -2609,7 +2611,7 @@ body.tooltips-off .maplibregl-popup { display: none !important; }
2609
  top: 50%;
2610
  transform: translateY(-50%);
2611
  height: 3px;
2612
- background: oklch(100% 0 0 / 0.2);
2613
  border-radius: 2px;
2614
  pointer-events: none;
2615
  }
@@ -2644,8 +2646,8 @@ body.tooltips-off .maplibregl-popup { display: none !important; }
2644
  height: 14px;
2645
  border-radius: 50%;
2646
  background: #ffffff;
2647
- border: 2px solid oklch(15% 0.012 250);
2648
- box-shadow: 0 1px 4px oklch(0% 0 0 / 0.4);
2649
  cursor: pointer;
2650
  margin-top: 0;
2651
  }
@@ -2655,8 +2657,8 @@ body.tooltips-off .maplibregl-popup { display: none !important; }
2655
  height: 14px;
2656
  border-radius: 50%;
2657
  background: #ffffff;
2658
- border: 2px solid oklch(15% 0.012 250);
2659
- box-shadow: 0 1px 4px oklch(0% 0 0 / 0.4);
2660
  cursor: pointer;
2661
  }
2662
  .temporal-slider-input::-webkit-slider-runnable-track,
@@ -2670,12 +2672,12 @@ body.tooltips-off .maplibregl-popup { display: none !important; }
2670
  .temporal-slider-start { z-index: 1; }
2671
 
2672
  .temporal-year-label {
2673
- color: oklch(95% 0 0);
2674
  font-family: var(--font-mono);
2675
  font-variant-numeric: tabular-nums;
2676
  font-size: 12px;
2677
  font-weight: 500;
2678
- min-width: 110px;
2679
  text-align: center;
2680
  letter-spacing: -0.01em;
2681
  flex-shrink: 0;
 
3
  Renamed from 10.08_styles.css on 2026-05-20.
4
  ============================================================================ */
5
 
6
+ @import url('https://fonts.googleapis.com/css2?family=Source+Sans+3:wght@300;400;600;700&family=Barlow+Condensed:wght@500;600;700&family=Source+Code+Pro:wght@400;500;600&display=swap');
7
 
8
  /* -- Design tokens --------------------------------------------------------- */
9
  :root {
 
61
  /* Glass blur */
62
  --blur-glass: saturate(140%) blur(14px);
63
 
64
+ /* Typography — Rights Lab brand: Source Sans 3 (UI/body),
65
+ Barlow Condensed (display/labels/wordmark), Source Code Pro (data/coords) */
66
+ --font-ui: 'Source Sans 3', system-ui, -apple-system, sans-serif;
67
+ --font-mono: 'Source Code Pro', 'SF Mono', ui-monospace, monospace;
68
 
69
+ /* Condensed display face for labels + wordmark; body falls back to UI font */
70
+ --font-label: 'Barlow Condensed', 'Source Sans 3', system-ui, sans-serif;
71
  --font-body: var(--font-ui);
72
 
73
  /* Motion — Spatial spring */
74
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
75
+ --ease-spring: cubic-bezier(0.16, 1, 0.3, 1); /* calmed: ease-out, no overshoot */
76
  --t-fast: 140ms; --t-base: 220ms; --t-slow: 320ms;
77
 
78
  /* Radii */
 
422
  }
423
 
424
  .stat-val {
425
+ font-family: var(--font-ui);
426
  font-size: 30px;
427
+ font-weight: 700;
428
  font-variant-numeric: tabular-nums;
429
  line-height: 1;
430
  margin-bottom: 4px;
 
629
  }
630
 
631
  .ind-card:hover {
 
632
  box-shadow: var(--depth-2);
633
  }
634
 
 
1358
 
1359
  /* ---- Brand ---- */
1360
  .nav-brand {
1361
+ font-family: var(--font-label);
1362
+ font-size: 22px;
1363
+ font-weight: 700;
1364
+ letter-spacing: 0.14em;
1365
  text-transform: uppercase;
1366
  color: var(--c-text);
1367
  padding: 0 var(--space-2);
 
2513
  margin-left: auto;
2514
  margin-right: auto;
2515
  z-index: 950;
2516
+ background: var(--c-glass-2);
2517
+ backdrop-filter: var(--blur-glass);
2518
+ -webkit-backdrop-filter: var(--blur-glass);
2519
+ border: 1px solid var(--c-glass-stroke);
2520
  border-radius: var(--radius-md);
2521
  box-shadow: var(--depth-3);
2522
  padding: 6px 12px 8px;
 
2559
  .temporal-play-btn,
2560
  .temporal-reset-btn {
2561
  background: transparent;
2562
+ color: var(--c-text-mid);
2563
+ border: 1px solid var(--c-glass-stroke);
2564
  cursor: pointer;
2565
  font-family: var(--font-ui);
2566
  transition: background var(--t-fast) var(--ease-out),
 
2587
  }
2588
  .temporal-play-btn:hover,
2589
  .temporal-reset-btn:hover {
2590
+ background: var(--c-glass-hover);
2591
+ border-color: var(--c-glass-stroke-strong);
2592
+ color: var(--c-text);
2593
  }
2594
  .temporal-play-btn:focus-visible,
2595
  .temporal-reset-btn:focus-visible {
 
2611
  top: 50%;
2612
  transform: translateY(-50%);
2613
  height: 3px;
2614
+ background: var(--c-glass-stroke-strong);
2615
  border-radius: 2px;
2616
  pointer-events: none;
2617
  }
 
2646
  height: 14px;
2647
  border-radius: 50%;
2648
  background: #ffffff;
2649
+ border: 2px solid var(--c-accent);
2650
+ box-shadow: var(--depth-1);
2651
  cursor: pointer;
2652
  margin-top: 0;
2653
  }
 
2657
  height: 14px;
2658
  border-radius: 50%;
2659
  background: #ffffff;
2660
+ border: 2px solid var(--c-accent);
2661
+ box-shadow: var(--depth-1);
2662
  cursor: pointer;
2663
  }
2664
  .temporal-slider-input::-webkit-slider-runnable-track,
 
2672
  .temporal-slider-start { z-index: 1; }
2673
 
2674
  .temporal-year-label {
2675
+ color: var(--c-text);
2676
  font-family: var(--font-mono);
2677
  font-variant-numeric: tabular-nums;
2678
  font-size: 12px;
2679
  font-weight: 500;
2680
+ min-width: 150px;
2681
  text-align: center;
2682
  letter-spacing: -0.01em;
2683
  flex-shrink: 0;