5 min read
Data, inputs & naming
Data: cadence and constraints
- API data is constrained before it is drawn. Never display a returned string with a loose edge — everything that comes back from an API goes through the measure / fit / clip path so it cannot run off the canvas. No exceptions for "it's always short in practice."
refreshfollows the data, not the device: 60 s for clocks, 300 for transit and markets, 900–1800 for weather, 3600 for daily feeds.ttl_secondsonhttp.gettracks how fast each hop moves: 86400 for geocoding and grid lookups, down to 60 only for genuinely live feeds. Keeprefreshand the main fetch's ttl in sync, and say so in a comment.- Two-hop location is standard: zip →
api.zippopotam.us/us/<zip>(ttl 86400) → lat/lon → the real feed. Send a User-Agent to NWS. - Budget: max 8
http.getcalls per render — size the page count to it.
See HTTP requests for the mechanics.
Inputs
- Free text only for genuinely free text. If the value could be a picklist, it is one: dropdowns and option fields defined in the GDN source beat free text, because free text is where community error comes from. Free text is right when the value is displayed verbatim or passed to an API (a zip, a station ID, a key); a backend input that selects behavior is a dropdown.
- Every logic path an input combination allows must work. "Zip code OR station ID" means the app handles either one being absent — or a dropdown first asks which the user is providing, and the free text follows. No combination of filled / blank inputs may break the render.
- Every input is visibly used. If an app takes multiple inputs and shows one page, the page uses them all — or the app makes a page per input (never a random subset of the inputs' pages). Studio flags a declared input the code never reads as not used in code.
- Input keys are letters + digits only (
apikey, neverapi_key); API keys must beapp_input_type: api-key(encrypted) with a folderREADME.mdsaying where to get one. Defaults containing:are truncated; date pickers arrive as"2026-08-14T01"— parse digits. Optional blank-default inputs are read asctx.inputs.get("key", fallback). Reuse the shared 52-zonetimezonedropdown and thezipinput where they fit.
Every control, with a picture: User input types.
Naming, category & submission
- App names are descriptive of what the app actually is, and mutually exclusive — against every app in all of Glance, not just GDN. "Custom Sports Schedule" that is really a Scottish football schedule is named Scottish Football Schedule.
- No "SCROLL" or "LED" in app names. Apps are potentially shared across the whole
platform, so the device name doesn't belong in the app name. (Existing classic /
scroll pairs use a " (Scroll)" suffix in
name:to tell the builds apart — leave those as they are. This rule is about an app's own title: don't name a new app after the device it runs on.) - Every app lands in a real category — or a new category is requested. Other is a catch-all, not a good landing place.
gdn: 1
id: market-hours # lowercase-hyphen, equals the folder name
name: Market Hours # descriptive, unique across Glance, no device names
category: Finance # a real category - Other is a last resort
width: 64 # 64 / 128 / 192 / 384 - height always 32
refresh: 300
pages: [bell] # one def per name; 1-3 typical
Every field: manifest.yaml.
Comment culture
Non-obvious layout numbers carry a comment naming the collision or misread they fix, with the offending string and its measured width:
# 'TROP STORM' is 107px at 10x16, so it started at x=79 and the two
# drew through each other for 41px.
The best apps open with a # DESIGN. paragraph stating the visual thesis — which,
under these guidelines, is your design brief — before any code. Write both.