Guidance for working in the Shiny Controls repo (Shiny.Maui.Controls + Shiny.Blazor.Controls and their add-on packages).
src/— one project per package per host. Core:Shiny.Maui.Controls,Shiny.Blazor.Controls. Add-ons on both hosts:*.Barcodes,*.Camera,*.Camera.Ai,*.Diagram,*.FloorPlan,*.Gamepad,*.Markdown,*.MediaElement,*.MermaidDiagrams,*.Office,*.SpeechAddins, themes*.Themes.Aurora/Material/Ocean/Terminal. MAUI-only:Shiny.Maui.Controls.Desktop,Shiny.Maui.Controls.Keyframe(+.Keyframe.Export),Shiny.Maui.Controls.MediaElement.Linux, camera analyzersCamera.Barcode/Documents/Face/Motion/Ocr. Cross-host shared libraries (referenced by the hosts, also published):Shiny.Controls.Camera.Shared,.Diagram.Shared(the diagramming engine — the graph model, tidy tree / layered / mindmap / radial / force-directed layouts, connector routing and shape-outline geometry; dependency-free),.Gamepad.Shared(the on-screen gamepad engine — layouts, multi-touch hit testing, stick/d-pad maths, and theVirtualGamepad/VirtualGamepadManagerthat make it a Shiny.GamepadIGamepad),.FloorPlan.Shared(the floor plan engine — document model, Skia renderers, hit-testing, camera, tools; the reason FloorPlan is an add-on and not core is that it drags SkiaSharp with it),.Gantt.Shared(the Gantt scheduling engine — calendars, dependency cascades, critical path, time-to-pixel geometry),.RangePickers.Shared(the date/time range picker engine — DateRange/TimeRange/DateTimeRange, tap-to-select rules with min/max dates and spans and blocked days, month grids, presets, time slots with duration and overnight rules, compact culture-aware range formatting),.Keyboard.Shared(the keyboard shortcut engine — gesture parsing/display, Primary ⌘/Ctrl, layout-aware matching, scopes with modal blocking, chords, repeat/release, native key-code tables; the Blazor JS matcher inkeyboard-shortcuts.jsmirrors its rules),.Keyframe.Shared,.MediaElement.Shared,.MotionIcons.Shared,.Office.Shared,.Office.Skia. There is no*.Kioskpackage — the Blazor on-screen keyboard and docking ship inShiny.Blazor.Controls.samples/Sample/— the MAUI + Blazor demo app. Each control has a feature page undersamples/Sample/Features/<Area>/, wired intoAppShell.xamlandMauiProgram.cs.tests/— unit tests.SKILLS/shiny-controls/— the local skill (SKILL.md+ one markdown file per control) that teaches code generation for these controls.README.md— the front door: summary paragraph, NuGet badges, Getting Started, and a grouped index linking todocs/controls/.docs/controls/— one markdown file per control (datagrid.md,document-editor.md, …), named to matchSKILLS/shiny-controls/where a skill file exists, plusstyling.mdfor the cross-cutting styling/theming note. Images are referenced as../../assets/….themes/— M3 theme pack seeds.
The public docs live in a separate repo: ~/Desktop/dev/documentation (Astro / Starlight).
- Controls docs:
src/content/docs/controls/<control>/ - Controls release notes:
src/content/docs/controls/release-notes.mdx - Main menu (sidebar):
src/sidebar-topics.mjs(theControlstopic, ~line 289). The homepage menu (HomepageNav.astro) is auto-generated from this file — no separate edit needed. - Homepage:
src/content/docs/index.mdx— the "UI Controls"<Card>(~line 149) lists every control grouped by category (Flagship / Layout & Overlays / Input / Display & Media / Status & Feedback / Desktop).
With each fix and each new feature, update all of the following so they stay in sync:
docs/controls/<control>.md— reflect new/changed behavior in that control's own page (add the page and a row in the README's grouped index if the control is new). TouchREADME.mditself only for the summary paragraph, a NuGet badge, Getting Started, or the index.- Local skill (
SKILLS/shiny-controls/) — update the relevant control's.md(or add a new one and reference it inSKILL.md) so generated code matches. - Shiny docs (
~/Desktop/dev/documentation):- Release notes — add an entry to
src/content/docs/controls/release-notes.mdx. - Menu — for a new feature, add the new menu node(s) under the
Controlstopic insrc/sidebar-topics.mjs. (The homepage menu updates automatically from this.)
- Release notes — add an entry to
- Add its docs folder under
src/content/docs/controls/<control>/. - Add it to the homepage section — the "UI Controls"
<Card>insrc/content/docs/index.mdx(place it in the appropriate category group). - Add its top-level node to the main menu (
src/sidebar-topics.mjs) under theControlstopic — which also surfaces it in the homepage menu. - Leave a screenshot TODO — do not capture screenshots as part of the feature/release work. Instead, after wiring up a new component, note a TODO (e.g.
TODO: capture screenshots for <control>) so they can be done later on request. See Screenshots below. - Pass the Jon Dick Nitpick checklist (below) on both hosts before calling the control done.
- Add it to the solution (
Shiny.Controls.slnx) and toBuild.slnf. - Add a NuGet badge to
README.md— the badge block sits directly under the summary paragraph — and mention the package in the summary paragraph itself. - Add it to the
Packagedropdown in.github/ISSUE_TEMPLATE/bug_report.ymland to theTarget Packagedropdown in.github/ISSUE_TEMPLATE/feature_request.yml. Both dropdowns mirrorsrc/exactly, so a removed or renamed package must come out of them too. - Update the Repo layout bullet at the top of this file.
Things that get spotted as bugs straight away, so check every new control (and any reworked layout) for them on both hosts before it ships. Each one is a symptom of the control fighting the container it was put in rather than fitting it.
- No scroll region inside a scroll region (double scrollbars). A control that scrolls must not end up inside another scroller on the same axis: two scrollbars side by side, or a wheel or finger that scrolls the inner region until it hits the end and then jerks the outer one. Pick one owner per axis. If the control fills its parent, the control scrolls and the parent doesn't (give it a real height:
height:100%/flex:1withmin-height:0on Blazor, aGridrow of*on MAUI, never aScrollViewaround it). If it sizes to its content, it doesn't scroll and the page does. Watch for the usual causes: a scroller in the control's template plus one in the sample page, a Blazoroverflow:autoon both the root and an inner pane, a MAUICollectionView/ScrollViewplaced inside aScrollView, and a content region that is a few pixels too tall because of a border or padding, which adds a scrollbar that only moves 2px. - No double borders. When a control with its own border fills a parent that also draws one (a card, a
demo-section, aBorder, another Shiny control), the two edges sit together and read as one double-thick line. Only one of them may draw the edge. A control that is designed to fill its host should have a way to drop its own border or stroke (aBorder/ShowBorderparameter, or a CSS custom property / style token), and when one Shiny control hosts another, the inner one is turned off. The same goes for stackedborder-radius(a rounded inner edge inside a rounded outer edge with a gap of background between them) and for doubled dividers where two adjacent panes each draw the shared edge.
More to come — add new items here as they are found.
These are the platforms the issue templates offer and that features should be reasoned about against:
- MAUI — iOS, Android, Mac Catalyst, macOS (AppKit,
net10.0-macos), Windows (WinUI), Linux (GTK4). - Blazor — WebAssembly, Server, and Hybrid (MAUI
BlazorWebView).
Not every package covers every one; MAUI add-ons multi-target net10.0 plus some subset of -ios/-android/-maccatalyst/-macos, and the Windows TFM is only added on Windows or with -p:ForceWindowsTfm=true.
Capturing control screenshots is a separate, on-request task — never run it automatically as part of a fix, feature, or release. When a new control ships, just leave a TODO (see step 7 above). Only capture when the user explicitly asks.
When asked to capture screenshots for a control:
- MAUI — drive the demo app (
samples/Sample/) to the control's feature page (samples/Sample/Features/<Area>/) and capture the screenshot with mauidevflow. - Blazor — drive the Blazor sample to the matching page and capture the screenshot with Playwright.
- Add them to the docs repo (
~/Desktop/dev/documentation):- Save images under
public/images/<control>/, nameds1.png,s2.png, … (one folder per control, sequential names). - Reference them from the control's
.mdx(e.g.src/content/docs/controls/<control>/index.mdx) with<img src="/images/<control>/s1.png" width="220" alt="<Control>" />.
- Save images under
Do not write blog posts automatically as part of a fix/feature. Write them only when the user asks. When asked to blog a feature, produce two posts — first the docs-site version, then adapt it for the personal blog.
- File:
src/content/docs/blog/YYYY/MM/<slug>.mdx(current year/month folders; create the month folder if needed). - Frontmatter:
--- title: '...' description: '...' date: YYYY-MM-DD authors: - allanritchie tags: - Release # or Feature, AI, etc. ---
- Body is MDX. Reuse components where relevant, e.g.
import NugetBadge from '/src/components/NugetBadge.astro';then<NugetBadge name="Shiny.Maui.Controls.Xxx" />. - Voice: product/release-note tone — what shipped, breaking changes, code samples, how to use it. No hero image on this site.
- File:
src/content/blog/YYYY/MM/<slug>.mdx(note:content/blog, notcontent/docs/blog). - Frontmatter (different schema — see
src/content.config.ts):--- title: '...' description: '...' pubDate: 'Mon DD YYYY' # e.g. 'Jun 15 2026' heroImage: '../../../../assets/<slug>-hero.svg' tags: ['Shiny', '.NET MAUI', 'Controls'] ---
- Voice: rework the docs post into a personal, first-person narrative ("Here's something that shouldn't be hard but is…", "So I built…") — story/motivation up front, not a dry changelog.
- Hero image is required. Create
src/assets/<slug>-hero.svg:- SVG,
viewBox="0 0 1200 630",width="1200" height="630". - Match the house style: dark navy/indigo gradient background (
#0f172a→#1e1b4b), cyan/green/violet accent gradients, subtle glow filters, the feature name as the headline. Crib an existing one (e.g.datasync-hero.svg,documentdb-orleans-hero.svg) as a starting template.
- SVG,
- Keep MAUI and Blazor at feature parity where the platform allows; note platform-only features explicitly (Desktop and Keyframe are MAUI-only; SheetView, Layout/AppLayout, SplashScreen and the on-screen keyboard are Blazor-only, etc.).
- Add/update a demo page in
samples/Sample/Features/for any new control or notable feature. - Build with
dotnet build Build.slnf.