Everything in this section is "enabled" (or, for BMFT.Log, never gated at all) — live for every player in a real release build, today. Build against these without any extra setup or dev-only flag.
Sidebar sticky note (BMFT.StickyNotes)
Status: shipped "enabled". Two small widgets other mods can post day-limited text lines to, or that render the base game's own reminder text. Both render in the right sidebar at the start of each day, between the base game's location line and its own "It's a work day."-style status text — nothing else in Core hooks that spot:
- A theme-styled notices banner (built from the same CSS custom properties the base game's own "Morning"/"Daytime"/"Evening" activity-slot header uses —
--theme-link-color,--theme-link-text-color,--border-radius-xcl-lg,--shadow-xcl-sm,--text-shadow-xcl-sm— so it honors whatever theme is active rather than a hand-picked color) showing the base game's own "today request" reminder text, one line per sentence, in the order it was received — not addon content, and not something you post to directly. - A post-it note below it, showing the 5 most-recently-posted messages from your own mod (or any other addon) via
post()/(bmft_sticky_note_post:). If more than 5 are simultaneously active, the rest render as up to 2 additional post-it notes further down the sidebar, below the button row (a real, base-game-provided extension point,right_post_options) — see "Multiple addons / overflow notes" below.
They're kept visually distinct on purpose (different shape, different "sticky tape" angle across the top of each) so the banner reads as "the game telling you something" and the post-it reads as "a note someone left," rather than one shared board.
The base game's own "today request" reminder text (fitness/charm decay warnings, "It's a work day.", scheduled demos, and so on) always renders on the notices banner, never mixed into the post-it note. today request (refs/x-change-source) builds its reminders as a proper list but joins them with a single space before rendering ((joinlinesspace:...)) -- Core re-splits that joined text back into individual lines on sentence boundaries (period/!/?) so the banner still reads as one item per line. This isn't a perfect inverse of the original list (a single base-game reminder that happens to contain two sentences splits into two lines here), but there's no separator left in the captured text to tell those cases apart. You don't do anything to make this happen; it's automatic.
Reading the notices banner
const notices = window.BMFT.StickyNotes.activeNotices(); // string[], in the order receivedMirrors activeLines() (below) for the post-it board -- read-only, no post()-equivalent: the banner's content always comes from the base game's own captured text, not from a call your mod makes.
Because of the notices banner, something can appear even with zero addons installed, on any day the base game has non-empty reminder text of its own. That's intentional, not a loophole: restyling content the base game already shows is a pure presentational improvement, not new mechanics or a dead-weight feature waiting on an addon that might never come — see CLAUDE.md's "Scope guardrail" section for the actual test Core's own rule applies (never worse, never dead weight — not literally zero observable change). What still renders exactly as it would with this mod absent: genuinely nothing to show (no base reminder text and no addon posts) and the sticky_notes feature being off, both of which fall back to the base game's own plain text, unstyled.
Posting from your own Twee content
(bmft_sticky_note_post: "Callie will be at the gym today.", 1)- First argument is the message text — a non-empty string, or the call throws (a caller mistake, not a state to silently accept).
- Second argument is how many day-starts it should stay visible, including the day it's posted —
1shows it only today,3shows it today plus the next two. Must be a positive integer, or the call throws. - Posting the exact same message text again refreshes its expiry to the new call's duration instead of adding a second line — the note is keyed on message text itself, so exact duplicates are deduped by construction, not filtered after the fact. If you want two separate lines, make the text different (even a trailing detail like a date or count is enough).
Posting from your own JS
window.BMFT.StickyNotes.post("Rent is due tomorrow.", 2);Same macro, same behavior, callable directly from JS if you're not in a Twee passage.
Reading what's currently posted
const lines = window.BMFT.StickyNotes.activeLines(); // string[], most-recently-posted first, up to MAX_TOTALUseful if your own UI wants to show the same content elsewhere. You don't need this just to post — the sidebar rendering is automatic.
Multiple addons / overflow notes
Built for multiple mod authors posting to the same board at once (2026-08-20, explicit product decision), not just one mod's own messages:
- Across ALL addon posts combined, at most
BMFT.StickyNotes.MAX_TOTAL(15) render at once, newest-first — same sortactiveLines()already used, just extended past one note's worth. More than 15 simultaneously-active messages can still be tracked (each still expires on its own schedule), but only the 15 most recent ever render — FIFO: the oldest are the first to fall off once the cap is exceeded. (Reduced from an initial 20 the same day once playtesting showed the 20-note stack read as too dense.) - The first
MAX_VISIBLE(5) render in the note directly below the notices banner. The 6th message onward renders as up to 2 additional post-it notes below the button row (right_post_options, a real hook the base game's own "day right sidebar" already leaves empty at that exact spot — the same one the base game's own "Back" link uses on other screens, never this one, so there's no collision),MAX_VISIBLEper note.MAX_TOTAL(15) /MAX_VISIBLE(5) is exactly 3 notes' worth — 1 above the button row, 2 below — matching the requested layout ("including the top game note we should have 1 above and 2 below the buttons"). - All 3 addon notes randomize their look (color, rotation, tape angle/position) from a small palette of classic sticky-note colors — including the first/fixed one, which originally always stayed yellow (changed 2026-08-20). The look re-randomizes on every save load — wired to the base game's own
xlowe:state-restoredDOM event (refs/x-change-source'ssrc/scripts/10-init/00-custom-save-game.js:1557-1568,1808, fired withdetail.source === "save"specifically when a save finishes loading; the base game's own02-gameplay-menu.js:1476-1483listens for the same event/source the same way) — and stays fixed between loads, so it doesn't shuffle on every re-render, only when a save actually loads.BMFT.StickyNotes.reseedNoteLook()re-rolls it manually if you ever need to from your own code. - If the overflow hook can't be found in the DOM (structurally shouldn't happen — see
renderTodaySidebar()'s own comment for why it's expected to always exist alongside the hooks this feature already uses), awarn-levelBMFT.Logbreadcrumb fires and those notes are simply not rendered, rather than throwing.
Appearance
Text renders at 0.78rem in every widget (fixed note, overflow notes, and notices banner alike) -- matching the base game's own small-note-text convention (.handbag-menu-slot/ .handbag-menu-return, refs/x-change-source's stylesheet.css:2682,2700, both 0.78rem -- the next size down from .handbag-menu-note's 0.82rem in the same stylesheet neighborhood), not an arbitrary size. rem (not em) ties it to html's own font-size, which is already accessibility-scaled (see docs/arch/12-theme-and-accessibility-css.md in the xls umbrella repo), so this still honors a player's Font Size setting. Line-height is 1.15 and vertical padding 0.45em in both widgets (tightened from an initial 1.5/0.6em 2026-08-21, over concern for smaller-resolution screens where a wrapped line pushes the button row further down) -- 1.15 matches the same .handbag-menu-* siblings' own line-height (1.1-1.18), not an arbitrary tightening either.
The post-it renders solid color, drop shadow, a slight rotation, handwritten-style font, left-aligned text, and a small strip of "tape" across its top. The notices banner renders as a theme-colored rounded card (its border/background/text color match the base game's own "Morning"/"Daytime"/"Evening" header) but keeps the same handwritten font as the post-it, not that header's bold serif — theme colors, handwritten font, on purpose, so the two read as one person's handwriting on two different notes rather than unrelated widgets. Its own, differently-angled tape strip, left-aligned text. Neither is currently player-customizable (color/alignment/rotation live in one spot each internally, noteMarkupAt() and noticesMarkup() in scripts/00-sticky-notes.js, should that change later) — the addon notes' color IS randomized per save load per the "Multiple addons / overflow notes" section above, but that's an automatic look, not a setting.
One-shot toasts (no persistence)
If you just want to flash a notification the moment something happens -- not add a standing line to the note -- use notify() instead of post():
(bmft_sticky_note_notify: "Pills go on sale today.")window.BMFT.StickyNotes.notify("Pills go on sale today.");This calls the base game's own sticky toast notification directly (GE.showNotification, duration: 0 -- stays up until dismissed, doesn't auto-fade) and never touches the persistent note or activeLines(). Use post() for "should stay visible for a while," notify() for "this just happened."
What to check after using this
- Run
validate_modagainst your mod to confirm(bmft_sticky_note_post:)/(bmft_sticky_note_notify:)resolve as real macros. - This always works — it's always registered, and if the sticky-notes feature is currently off inside Core, posting silently does nothing and your own lines don't render, rather than erroring. You never get an "unknown macro" error and never need to check anything first.