From beede6fa46441e7321241b1e7e90f08c338825aa Mon Sep 17 00:00:00 2001 From: Uriah Finkel Date: Tue, 11 Aug 2026 10:39:12 +0300 Subject: [PATCH 01/10] integrate Python examples into Great Docs --- examples/index.qmd | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 examples/index.qmd diff --git a/examples/index.qmd b/examples/index.qmd new file mode 100644 index 00000000..b724783c --- /dev/null +++ b/examples/index.qmd @@ -0,0 +1,13 @@ +--- +title: "Python Examples" +listing: + contents: posts + feed: true + sort: "date desc" + type: default + categories: true + sort-ui: false + filter-ui: false +page-layout: full +title-block-banner: true +--- From 26b780f63f0c941522ef0b643846c32910c6afe0 Mon Sep 17 00:00:00 2001 From: Uriah Finkel Date: Tue, 11 Aug 2026 10:39:22 +0300 Subject: [PATCH 02/10] migrate fixed-horizon example --- .../dca_with_fixed_time_horizons/index.qmd | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 examples/posts/dca_with_fixed_time_horizons/index.qmd diff --git a/examples/posts/dca_with_fixed_time_horizons/index.qmd b/examples/posts/dca_with_fixed_time_horizons/index.qmd new file mode 100644 index 00000000..5fe618a3 --- /dev/null +++ b/examples/posts/dca_with_fixed_time_horizons/index.qmd @@ -0,0 +1,62 @@ +--- +title: "Decision Curves with Fixed Time Horizons" +author: "Uriah Finkel" +date: "2025-12-15" +categories: [Decision, Time-Horizons] +--- + +`rtichoke` for Python introduces support for fixed time horizons, allowing flexible specification of the prediction horizon and automatically updating performance plots accordingly. + +## The most under-discussed design choice in prediction models: The Time Horizon + +Prediction models require a well-defined fixed time horizon: The end of follow-up over which the outcome probability is defined. A probability of dying within 1 week, 1 year, or 100 years represents fundamentally different clinical questions and very different implied decision contexts. + +It is therefore important to explore the sensitivity of model performance to the choice of time horizon: Shorter horizons typically yield fewer observed events, resulting in smaller gaps between the two baseline strategies "treat all" (Everyone is considered Predicted Positive) and "treat none" (everyone is considered Predicted Negative). + +In contrast, longer horizons may introduce ambiguity through censoring (loss to follow-up) or competing events (events that preclude the primary outcome). + +## Pragmatic approach: Performance Sensitivity Analysis with rtichoke + +You do not need to develop a new or more complex model to overcome these problems, first you need to ensure if there's a problem at all and for which time horizons: + +You can reuse predictions trained for a specific horizon and evaluate their robustness across alternative fixed time horizons: This allows you to assess how performance changes as the effective follow-up window varies without retraining the model. + +## Load data and fit a Cox Regression + +```{python} +import pandas as pd +import lifelines + +df_time_to_cancer_dx = pd.read_csv( + "https://raw.githubusercontent.com/ddsjoberg/dca-tutorial/main/data/df_time_to_cancer_dx.csv" +) + +cph = lifelines.CoxPHFitter() +cph.fit( + df=df_time_to_cancer_dx, + duration_col="ttcancer", + event_col="cancer", + formula="age + famhistory + marker", +) + +cph_pred_vals = cph.predict_survival_function( + df_time_to_cancer_dx[["age", "famhistory", "marker"]], times=[1.5] +) + +df_time_to_cancer_dx["pr_failure18"] = [1 - val for val in cph_pred_vals.iloc[0, :]] +``` + +## Decision Curve with Multiple Fixed Time Horizons + +The `fixed_time_horizons` argument allows you to explicitly define the set of follow-up horizons to evaluate. + +```{python} +from rtichoke import create_decision_curve_times + +create_decision_curve_times( + probs={"full": df_time_to_cancer_dx["pr_failure18"]}, + reals=df_time_to_cancer_dx["cancer"], + times=df_time_to_cancer_dx["ttcancer"], + fixed_time_horizons=[0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0] +) +``` From 9b2d0d43d145e2348bf5470059b13df4ee7da2a8 Mon Sep 17 00:00:00 2001 From: Uriah Finkel Date: Tue, 11 Aug 2026 10:39:35 +0300 Subject: [PATCH 03/10] migrate calibration example --- examples/posts/calibration_curve/index.qmd | 103 +++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 examples/posts/calibration_curve/index.qmd diff --git a/examples/posts/calibration_curve/index.qmd b/examples/posts/calibration_curve/index.qmd new file mode 100644 index 00000000..b9911f9f --- /dev/null +++ b/examples/posts/calibration_curve/index.qmd @@ -0,0 +1,103 @@ +--- +title: "Calibration Curves for Multiple Models" +author: "Uriah Finkel" +date: "2024-05-15" +description: "An example of creating a calibration curve using rtichoke and scikit-learn." +categories: [calibration, scikit-learn] +draft: false +--- + +The following example is inspired by the [scikit-learn documentation displaying a calibration curve](https://scikit-learn.org/stable/auto_examples/calibration/plot_calibration_curve.html). + +## Load data and fit models + +```{python} +from sklearn.datasets import make_classification +from sklearn.model_selection import train_test_split +from sklearn.linear_model import LogisticRegression +from sklearn.naive_bayes import GaussianNB +from sklearn.calibration import CalibratedClassifierCV +import numpy as np +from sklearn.svm import LinearSVC + +X, y = make_classification( + n_samples=10_000, n_features=20, n_informative=2, n_redundant=10, random_state=42 +) + +X_train, X_test, y_train, y_test = train_test_split( + X, y, test_size=0.99, random_state=42 +) + +lr = LogisticRegression(C=1.0) +gnb = GaussianNB() +gnb_isotonic = CalibratedClassifierCV(gnb, cv=2, method="isotonic") +gnb_sigmoid = CalibratedClassifierCV(gnb, cv=2, method="sigmoid") + +lr.fit(X_train, y_train) +gnb.fit(X_train, y_train) +gnb_isotonic.fit(X_train, y_train) +gnb_sigmoid.fit(X_train, y_train) + +y_proba_lr = lr.predict_proba(X_test)[:, 1] +y_proba_gnb = gnb.predict_proba(X_test)[:, 1] +y_proba_gnb_isotonic = gnb_isotonic.predict_proba(X_test)[:, 1] +y_proba_gnb_sigmoid = gnb_sigmoid.predict_proba(X_test)[:, 1] + +class NaivelyCalibratedLinearSVC(LinearSVC): + def fit(self, X, y): + super().fit(X, y) + df = self.decision_function(X) + self.df_min_ = df.min() + self.df_max_ = df.max() + + def predict_proba(self, X): + df = self.decision_function(X) + calibrated_df = (df - self.df_min_) / (self.df_max_ - self.df_min_) + proba_pos_class = np.clip(calibrated_df, 0, 1) + proba_neg_class = 1 - proba_pos_class + return np.c_[proba_neg_class, proba_pos_class] + +svc = NaivelyCalibratedLinearSVC(max_iter=10_000) +svc_isotonic = CalibratedClassifierCV(svc, cv=2, method="isotonic") +svc_sigmoid = CalibratedClassifierCV(svc, cv=2, method="sigmoid") + +svc.fit(X_train, y_train) +svc_isotonic.fit(X_train, y_train) +svc_sigmoid.fit(X_train, y_train) + +y_proba_svc = svc.predict_proba(X_test)[:, 1] +y_proba_svc_isotonic = svc_isotonic.predict_proba(X_test)[:, 1] +y_proba_svc_sigmoid = svc_sigmoid.predict_proba(X_test)[:, 1] +``` + +## Gaussian Naive Bayes + +```{python} +from rtichoke import create_calibration_curve + +create_calibration_curve( + probs={ + "Logistic": y_proba_lr, + "Naive Bayes": y_proba_gnb, + "Naive Bayes + Isotonic": y_proba_gnb_isotonic, + "Naive Bayes + Sigmoid": y_proba_gnb_sigmoid, + }, + reals=y_test +).show(config={"displayModeBar": False, "displaylogo": False}) +``` + +## Linear SVC + +```{python} +create_calibration_curve( + probs={ + "Logistic": y_proba_lr, + "SVC": y_proba_svc, + "SVC + Isotonic": y_proba_svc_isotonic, + "SVC + Sigmoid": y_proba_svc_sigmoid, + }, + reals=y_test +).show(config={"displayModeBar": False, "displaylogo": False}) +``` + +This reproduces the core comparison from the scikit-learn example while keeping the rtichoke rendering as the Python example itself. From 53a7798852292473f5e662bc45808ab87c136786 Mon Sep 17 00:00:00 2001 From: Uriah Finkel Date: Tue, 11 Aug 2026 10:39:44 +0300 Subject: [PATCH 04/10] add Examples blog section --- great-docs.yml | 86 +++++++++++++++++++++++++++----------------------- 1 file changed, 46 insertions(+), 40 deletions(-) diff --git a/great-docs.yml b/great-docs.yml index 8dcb1d78..ab4061c7 100644 --- a/great-docs.yml +++ b/great-docs.yml @@ -1,49 +1,55 @@ -display_name: rtichoke -parser: numpy -repo: https://github.com/uriahf/rtichoke_python -user_guide: user_guide -homepage: user_guide +display_name: rtichoke +parser: numpy +repo: https://github.com/uriahf/rtichoke_python +user_guide: user_guide +homepage: user_guide site_url: https://uriahf.github.io/rtichoke_python/ site: css: site.css +sections: + - title: Examples + dir: examples + type: blog + navbar_after: User Guide + source: - enabled: true - branch: main - placement: usage - -reference: - - title: Performance Data - desc: Prepare classification and time-to-event data for visualization. - contents: - - prepare_performance_data - - prepare_binned_classification_data - - prepare_performance_data_times - - prepare_binned_classification_data_times - - - title: Discrimination - desc: ROC, precision-recall, gains, and lift visualizations. - contents: - - create_roc_curve - - create_roc_curve_times - - plot_roc_curve - - create_precision_recall_curve - - create_precision_recall_curve_times - - plot_precision_recall_curve - - create_gains_curve - - create_gains_curve_times - - plot_gains_curve - - create_lift_curve - - create_lift_curve_times - - plot_lift_curve - - - title: Calibration - desc: Calibration visualizations for classification and time-to-event models. - contents: - - create_calibration_curve - - create_calibration_curve_times - + enabled: true + branch: main + placement: usage + +reference: + - title: Performance Data + desc: Prepare classification and time-to-event data for visualization. + contents: + - prepare_performance_data + - prepare_binned_classification_data + - prepare_performance_data_times + - prepare_binned_classification_data_times + + - title: Discrimination + desc: ROC, precision-recall, gains, and lift visualizations. + contents: + - create_roc_curve + - create_roc_curve_times + - plot_roc_curve + - create_precision_recall_curve + - create_precision_recall_curve_times + - plot_precision_recall_curve + - create_gains_curve + - create_gains_curve_times + - plot_gains_curve + - create_lift_curve + - create_lift_curve_times + - plot_lift_curve + + - title: Calibration + desc: Calibration visualizations for classification and time-to-event models. + contents: + - create_calibration_curve + - create_calibration_curve_times + - title: Utility desc: Decision-curve analysis for classification and time-to-event models. contents: From 39cf9a81d2f4c50c2ee03cea29e8e92ffa6e146e Mon Sep 17 00:00:00 2001 From: Uriah Finkel Date: Tue, 11 Aug 2026 10:40:02 +0300 Subject: [PATCH 05/10] match Examples styling to rtichoke blog --- site.css | 190 +++++++++---------------------------------------------- 1 file changed, 31 insertions(+), 159 deletions(-) diff --git a/site.css b/site.css index 14644969..0a518a62 100644 --- a/site.css +++ b/site.css @@ -20,165 +20,37 @@ --rtichoke-shadow: 0 10px 28px rgba(96, 46, 27, 0.07); } -body { - background: var(--rtichoke-bg); - color: var(--rtichoke-text); - font-family: "Commissioner", system-ui, sans-serif; - font-weight: 400; - letter-spacing: 0.005em; -} - -h1, -h2, -h3, -h4, -h5, -h6, -.navbar-brand, -.quarto-title-block .title, -.sidebar-title { - color: var(--rtichoke-primary); - font-family: "Fraunces9pt-Light", "Fraunces", Georgia, serif; - font-weight: 700; - letter-spacing: -0.015em; -} - -a { - color: var(--rtichoke-accent); - text-decoration-color: rgba(206, 61, 21, 0.35); - text-decoration-thickness: 0.1em; - text-underline-offset: 0.14em; -} - -a:hover, -a:focus-visible { - color: var(--rtichoke-primary); - text-decoration-color: currentColor; -} - -.navbar, -.navbar-dark, -.navbar-light { - background: rgba(255, 247, 245, 0.96) !important; - border-bottom: 1px solid var(--rtichoke-border); - box-shadow: 0 5px 18px rgba(96, 46, 27, 0.05); - backdrop-filter: blur(12px); -} - -.navbar-brand, -.navbar .nav-link, -.navbar .navbar-title { - color: var(--rtichoke-primary) !important; -} - -.navbar .nav-link:hover, -.navbar .nav-link:focus, -.navbar .nav-link.active { - color: var(--rtichoke-accent) !important; -} - -#quarto-sidebar, -.sidebar, -.quarto-secondary-nav { - background: var(--rtichoke-bg); - border-color: var(--rtichoke-border); -} - -.sidebar-item a, -.sidebar-navigation a { - border-radius: 0.55rem; -} - -.sidebar-item a:hover, -.sidebar-item a.active, -.sidebar-navigation a:hover, -.sidebar-navigation a.active { - background: var(--rtichoke-soft); - color: var(--rtichoke-accent) !important; -} - -.quarto-title-block, -.gd-group-card, -.gd-api-card, -.card, -.callout { - background: var(--rtichoke-surface); - border-color: var(--rtichoke-border) !important; - border-radius: 0.9rem; - box-shadow: var(--rtichoke-shadow); -} - -.quarto-title-block { - padding: 1.25rem 1.4rem; -} - -blockquote { - background: var(--rtichoke-soft); - border-left: 4px solid var(--rtichoke-primary); - border-radius: 0 0.7rem 0.7rem 0; - color: #523d35; - padding: 0.85rem 1.1rem; -} - -div.sourceCode, -pre, -code:not(.sourceCode) { - border-color: var(--rtichoke-border) !important; -} - -div.sourceCode, -pre { - background: #fffdfc; - border-radius: 0.75rem; - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.8); -} - -code:not(.sourceCode) { - background: var(--rtichoke-soft); - color: #8f321b; - border-radius: 0.35rem; -} - -.table, -table { - --bs-table-bg: transparent; - --bs-table-striped-bg: rgba(254, 240, 236, 0.72); - border-color: var(--rtichoke-border); -} - -.badge, -.btn-primary { - background-color: var(--rtichoke-primary) !important; - border-color: var(--rtichoke-primary) !important; -} - -.btn-outline-primary { - color: var(--rtichoke-primary); - border-color: var(--rtichoke-primary); -} - -.btn-outline-primary:hover { - background-color: var(--rtichoke-primary); - color: #fff; -} - -.page-footer, -footer.footer { - background: var(--rtichoke-surface); - border-top: 1px solid var(--rtichoke-border); - color: var(--rtichoke-muted); -} +body { background: var(--rtichoke-bg); color: var(--rtichoke-text); font-family: "Commissioner", system-ui, sans-serif; font-weight: 400; letter-spacing: 0.005em; } +h1, h2, h3, h4, h5, h6, .navbar-brand, .quarto-title-block .title, .sidebar-title, .listing-title { color: var(--rtichoke-primary); font-family: "Fraunces9pt-Light", "Fraunces", Georgia, serif; font-weight: 700; letter-spacing: -0.015em; } +a { color: var(--rtichoke-accent); text-decoration-color: rgba(206, 61, 21, 0.35); text-decoration-thickness: 0.1em; text-underline-offset: 0.14em; } +a:hover, a:focus-visible { color: var(--rtichoke-primary); text-decoration-color: currentColor; } +.navbar, .navbar-dark, .navbar-light { background: rgba(255, 247, 245, 0.96) !important; border-bottom: 1px solid var(--rtichoke-border); box-shadow: 0 5px 18px rgba(96, 46, 27, 0.05); backdrop-filter: blur(12px); } +.navbar-brand, .navbar .nav-link, .navbar .navbar-title { color: var(--rtichoke-primary) !important; } +.navbar .nav-link:hover, .navbar .nav-link:focus, .navbar .nav-link.active { color: var(--rtichoke-accent) !important; } +#quarto-sidebar, .sidebar, .quarto-secondary-nav { background: var(--rtichoke-bg); border-color: var(--rtichoke-border); } +.sidebar-item a, .sidebar-navigation a { border-radius: 0.55rem; } +.sidebar-item a:hover, .sidebar-item a.active, .sidebar-navigation a:hover, .sidebar-navigation a.active { background: var(--rtichoke-soft); color: var(--rtichoke-accent) !important; } +.quarto-title-block, .gd-group-card, .gd-api-card, .card, .callout { background: var(--rtichoke-surface); border-color: var(--rtichoke-border) !important; border-radius: 0.9rem; box-shadow: var(--rtichoke-shadow); } +.quarto-title-block { padding: 1.25rem 1.4rem; } + +/* Keep the integrated Examples section visually continuous with rtichoke blog. */ +.quarto-listing { display: grid; gap: 1.5rem; } +.quarto-listing .listing-item { background: var(--rtichoke-surface); border: 1px solid var(--rtichoke-border); border-radius: 18px; padding: 1.4rem 1.6rem; box-shadow: 0 14px 30px rgba(0, 0, 0, 0.05); transition: transform 0.15s ease, box-shadow 0.15s ease; } +.quarto-listing .listing-item:hover { transform: translateY(-2px); box-shadow: 0 18px 38px rgba(0, 0, 0, 0.08); } +.listing-description { color: #4b3a32; margin-bottom: 0.75rem; } +.listing-categories .listing-category, .quarto-category { background: var(--rtichoke-soft); color: var(--rtichoke-accent); border: 1px solid var(--rtichoke-border); padding: 0.25rem 0.55rem; border-radius: 999px; font-size: 0.85rem; font-weight: 600; } +.listing-date { color: #7b685f; font-weight: 600; letter-spacing: 0.02em; } + +blockquote { background: var(--rtichoke-soft); border-left: 4px solid var(--rtichoke-primary); border-radius: 0 0.7rem 0.7rem 0; color: #523d35; padding: 0.85rem 1.1rem; } +div.sourceCode, pre, code:not(.sourceCode) { border-color: var(--rtichoke-border) !important; } +div.sourceCode, pre { background: #fffdfc; border-radius: 0.75rem; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.8); } +code:not(.sourceCode) { background: var(--rtichoke-soft); color: #8f321b; border-radius: 0.35rem; } +.table, table { --bs-table-bg: transparent; --bs-table-striped-bg: rgba(254, 240, 236, 0.72); border-color: var(--rtichoke-border); } +.badge, .btn-primary { background-color: var(--rtichoke-primary) !important; border-color: var(--rtichoke-primary) !important; } +.btn-outline-primary { color: var(--rtichoke-primary); border-color: var(--rtichoke-primary); } +.btn-outline-primary:hover { background-color: var(--rtichoke-primary); color: #fff; } +.page-footer, footer.footer { background: var(--rtichoke-surface); border-top: 1px solid var(--rtichoke-border); color: var(--rtichoke-muted); } @media (prefers-color-scheme: dark) { - body.quarto-dark { - --rtichoke-bg: #211815; - --rtichoke-surface: #2d211d; - --rtichoke-soft: #3a2721; - --rtichoke-primary: #f29a7f; - --rtichoke-accent: #ffad91; - --rtichoke-border: #684235; - --rtichoke-text: #fff4f0; - --rtichoke-muted: #d5b9af; - --rtichoke-shadow: none; - } + body.quarto-dark { --rtichoke-bg: #211815; --rtichoke-surface: #2d211d; --rtichoke-soft: #3a2721; --rtichoke-primary: #f29a7f; --rtichoke-accent: #ffad91; --rtichoke-border: #684235; --rtichoke-text: #fff4f0; --rtichoke-muted: #d5b9af; --rtichoke-shadow: none; } } From 392a194c84a3ac9b51b65e59d701cc9b908e4af2 Mon Sep 17 00:00:00 2001 From: Uriah Finkel Date: Tue, 11 Aug 2026 10:46:07 +0300 Subject: [PATCH 06/10] match blog navbar on Examples pages --- blog-navbar.html | 73 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 blog-navbar.html diff --git a/blog-navbar.html b/blog-navbar.html new file mode 100644 index 00000000..3a0e4036 --- /dev/null +++ b/blog-navbar.html @@ -0,0 +1,73 @@ + From bc693eac6fd60b4e57a07fd083465bfaae5aad08 Mon Sep 17 00:00:00 2001 From: Uriah Finkel Date: Tue, 11 Aug 2026 10:46:18 +0300 Subject: [PATCH 07/10] load blog navbar shell --- great-docs.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/great-docs.yml b/great-docs.yml index ab4061c7..67736fe7 100644 --- a/great-docs.yml +++ b/great-docs.yml @@ -4,6 +4,8 @@ repo: https://github.com/uriahf/rtichoke_python user_guide: user_guide homepage: user_guide site_url: https://uriahf.github.io/rtichoke_python/ +include_in_header: + - file: blog-navbar.html site: css: site.css From 12db43c3fa8e569fc2552fdd9bbb3590daf19659 Mon Sep 17 00:00:00 2001 From: Uriah Finkel Date: Tue, 11 Aug 2026 10:46:44 +0300 Subject: [PATCH 08/10] hide docs chrome in blog shell --- site.css | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/site.css b/site.css index 0a518a62..760c2a79 100644 --- a/site.css +++ b/site.css @@ -14,8 +14,8 @@ --rtichoke-soft: #fef0ec; --rtichoke-primary: #c54b29; --rtichoke-accent: #ce3d15; - --rtichoke-border: #f0cfc3; - --rtichoke-text: #281d19; + --rtichoke-border: #f4d3c7; + --rtichoke-text: #1f1a17; --rtichoke-muted: #725f57; --rtichoke-shadow: 0 10px 28px rgba(96, 46, 27, 0.07); } @@ -24,7 +24,7 @@ body { background: var(--rtichoke-bg); color: var(--rtichoke-text); font-family: h1, h2, h3, h4, h5, h6, .navbar-brand, .quarto-title-block .title, .sidebar-title, .listing-title { color: var(--rtichoke-primary); font-family: "Fraunces9pt-Light", "Fraunces", Georgia, serif; font-weight: 700; letter-spacing: -0.015em; } a { color: var(--rtichoke-accent); text-decoration-color: rgba(206, 61, 21, 0.35); text-decoration-thickness: 0.1em; text-underline-offset: 0.14em; } a:hover, a:focus-visible { color: var(--rtichoke-primary); text-decoration-color: currentColor; } -.navbar, .navbar-dark, .navbar-light { background: rgba(255, 247, 245, 0.96) !important; border-bottom: 1px solid var(--rtichoke-border); box-shadow: 0 5px 18px rgba(96, 46, 27, 0.05); backdrop-filter: blur(12px); } +.navbar, .navbar-dark, .navbar-light { background: var(--rtichoke-bg) !important; border-bottom: 1px solid var(--rtichoke-border); box-shadow: 0 6px 20px rgba(0, 0, 0, 0.04); } .navbar-brand, .navbar .nav-link, .navbar .navbar-title { color: var(--rtichoke-primary) !important; } .navbar .nav-link:hover, .navbar .nav-link:focus, .navbar .nav-link.active { color: var(--rtichoke-accent) !important; } #quarto-sidebar, .sidebar, .quarto-secondary-nav { background: var(--rtichoke-bg); border-color: var(--rtichoke-border); } @@ -33,9 +33,25 @@ a:hover, a:focus-visible { color: var(--rtichoke-primary); text-decoration-color .quarto-title-block, .gd-group-card, .gd-api-card, .card, .callout { background: var(--rtichoke-surface); border-color: var(--rtichoke-border) !important; border-radius: 0.9rem; box-shadow: var(--rtichoke-shadow); } .quarto-title-block { padding: 1.25rem 1.4rem; } -/* Keep the integrated Examples section visually continuous with rtichoke blog. */ +/* The Examples area should feel exactly like the shared rtichoke blog shell. */ +.rtichoke-blog-shell #quarto-search, +.rtichoke-blog-shell .gd-github-widget, +.rtichoke-blog-shell .gd-version-badge, +.rtichoke-blog-shell .gd-dark-mode-toggle, +.rtichoke-blog-shell .gd-keyboard-nav, +.rtichoke-blog-shell .navbar .quarto-navbar-tools { display: none !important; } +.rtichoke-blog-shell .navbar-brand { font-family: "Fraunces", "Fraunces9pt-Light", serif; font-weight: 600; } +.rtichoke-blog-shell .navbar-nav .nav-link { font-family: "Fraunces", "Fraunces9pt-Light", serif; font-weight: 600; } +.rtichoke-blog-shell .quarto-title-block { background: transparent; border: 0; box-shadow: none; padding: 0; } +.rtichoke-blog-shell .quarto-listing-filter, +.rtichoke-blog-shell .quarto-listing .quarto-listing-filter, +.rtichoke-blog-shell .quarto-listing .listing-actions, +.rtichoke-blog-shell .quarto-listing .listing-search, +.rtichoke-blog-shell .quarto-listing .listing-tools, +.rtichoke-blog-shell .quarto-listing .listing-toolbar { display: none !important; } + .quarto-listing { display: grid; gap: 1.5rem; } -.quarto-listing .listing-item { background: var(--rtichoke-surface); border: 1px solid var(--rtichoke-border); border-radius: 18px; padding: 1.4rem 1.6rem; box-shadow: 0 14px 30px rgba(0, 0, 0, 0.05); transition: transform 0.15s ease, box-shadow 0.15s ease; } +.quarto-listing .listing-item { background: #ffffff; border: 1px solid var(--rtichoke-border); border-radius: 18px; padding: 1.4rem 1.6rem; box-shadow: 0 14px 30px rgba(0, 0, 0, 0.05); transition: transform 0.15s ease, box-shadow 0.15s ease; } .quarto-listing .listing-item:hover { transform: translateY(-2px); box-shadow: 0 18px 38px rgba(0, 0, 0, 0.08); } .listing-description { color: #4b3a32; margin-bottom: 0.75rem; } .listing-categories .listing-category, .quarto-category { background: var(--rtichoke-soft); color: var(--rtichoke-accent); border: 1px solid var(--rtichoke-border); padding: 0.25rem 0.55rem; border-radius: 999px; font-size: 0.85rem; font-weight: 600; } From f79967ec5b7d14e081ee47ca472d6519e27ca9d0 Mon Sep 17 00:00:00 2001 From: Uriah Finkel Date: Tue, 11 Aug 2026 10:52:35 +0300 Subject: [PATCH 09/10] inline blog navbar injection --- great-docs.yml | 70 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/great-docs.yml b/great-docs.yml index 67736fe7..29791899 100644 --- a/great-docs.yml +++ b/great-docs.yml @@ -5,7 +5,75 @@ user_guide: user_guide homepage: user_guide site_url: https://uriahf.github.io/rtichoke_python/ include_in_header: - - file: blog-navbar.html + - text: | + site: css: site.css From a0feb62daeb98f3fbb0aa655e5bafb17de88f51c Mon Sep 17 00:00:00 2001 From: Uriah Finkel Date: Tue, 11 Aug 2026 10:55:07 +0300 Subject: [PATCH 10/10] replace Examples navbar with rtichoke blog shell --- great-docs.yml | 77 ++++++++++++++++---------------------------------- 1 file changed, 25 insertions(+), 52 deletions(-) diff --git a/great-docs.yml b/great-docs.yml index 29791899..28ac2c23 100644 --- a/great-docs.yml +++ b/great-docs.yml @@ -19,59 +19,32 @@ include_in_header: const nav = document.querySelector('nav.navbar'); if (!nav) return; - const brand = nav.querySelector('.navbar-brand'); - if (brand) { - brand.textContent = 'rtichoke blog'; - brand.setAttribute('href', 'https://rtichoke-blog.netlify.app/'); - brand.setAttribute('aria-label', 'rtichoke blog home'); - } + const container = nav.querySelector('.navbar-container'); + if (!container) return; - const navLists = [...nav.querySelectorAll('ul.navbar-nav')]; - if (!navLists.length) return; - const mainList = navLists.find((el) => el.classList.contains('me-auto')) || navLists[0]; - const rightList = navLists.find((el) => el.classList.contains('ms-auto')) || navLists[navLists.length - 1]; - if (mainList !== rightList) mainList.replaceChildren(); - - const links = [ - ['R examples', 'https://rtichoke-blog.netlify.app/posts'], - ['Python examples', examplesRoot], - ['Talks', 'https://rtichoke-blog.netlify.app/talks.qmd'], - ['About Me', 'https://rtichoke-blog.netlify.app/about_me'] - ]; - const iconLinks = [ - ['github', 'https://github.com/uriahf'], - ['twitter-x', 'https://twitter.com/finkeluriah'], - ['facebook', 'https://www.facebook.com/groups/rforthemasses'], - ['linkedin', 'https://www.linkedin.com/in/uriah-finkel'], - ['telegram', 'https://t.me/as_if_uriah'], - ['rss', 'https://rtichoke-blog.netlify.app/posts.xml'] - ]; - - rightList.replaceChildren(); - for (const [label, href] of links) { - const li = document.createElement('li'); - li.className = 'nav-item'; - const a = document.createElement('a'); - a.className = 'nav-link'; - a.href = href; - a.textContent = label; - if (label === 'Python examples') a.classList.add('active'); - li.appendChild(a); - rightList.appendChild(li); - } - for (const [icon, href] of iconLinks) { - const li = document.createElement('li'); - li.className = 'nav-item compact'; - const a = document.createElement('a'); - a.className = 'nav-link'; - a.href = href; - a.setAttribute('aria-label', icon); - const i = document.createElement('i'); - i.className = `bi bi-${icon}`; - a.appendChild(i); - li.appendChild(a); - rightList.appendChild(li); - } + container.innerHTML = ` + + + `; });