CheckCourt
App platform

Design guidelines

How your extensions, copy and marketplace listing fit into CheckCourt

Clubs shouldn't notice where CheckCourt ends and your app begins. A good extension looks like part of the page it sits on: same colors, same font, same tone. This page sums up what we look for.

Part of the review

The review item Erweiterungen entsprechen den Design- und Textregeln (extensions follow the design and copy rules) in Review and marketplace refers to this page. At the end there is a checklist.

Declarative first

For every extension, start with the declarative form. CheckCourt renders it with its own design system, in light and dark mode, on phones and at the kiosk. You don't have to take care of colors, spacing, focus or keyboard, and the review goes faster.

An iframe is only worth it when the blocks really aren't enough: a calendar, a map, a chart, an editing flow with many intermediate steps. On the dashboard, the booking button, the kiosk and the host surfaces (court badges, member list columns, booking hints, plan and sidebar buttons), only the declarative form is allowed anyway.

You want to …Use
Show numbers, a status, short lists, a few detailsdeclarative
Toggle a setting, submit a short formdeclarative
Trigger an action on a bookingdeclarative (booking.action)
Show a chart, a map, your own calendariframe
A longer editing flow with several stepsiframe, or a link to your own application

Where your extension appears

CheckCourt puts every panel in its own card, without your app icon, app name or an „App“ badge, so the card reads like a part of CheckCourt. The only title is the optional label from your manifest entry; without it the card has none. Don't bring the name and logo back in the content either. Start right away with what the person wants to know. Since nothing marks the card as an app's, it must never imitate CheckCourt's own controls to mislead people, such as a fake sign-in or payment prompt; the developer terms forbid it. The compact host surfaces have no card: badges, cells and hints sit right inside CheckCourt's own screens, so they have to read like CheckCourt wrote them.

PointContextSpaceWhat fits well
booking.detail.panelBooking details dialognarrow: the dialog is at most 448 pixels wide, on phones the screen widthtwo to five details about the booking, at most one button
booking.actionButton next to the booking's other actionsone label, up to 40 charactersa verb with an object: „Licht einschalten“ (turn on lights), „Platz freigeben“ (release court)
member.profile.sectionMember page in the admin area and the member's settingsfull page widthdetails about the person from your system, one setting
dashboard.widgetDashboard, in a two-column grid from tablet width uphalf a column, full width on phonesone number or status with one sentence of context
app.settingsAdmin → Apps → your app, below the settings formfull page widthconnection status, mappings, test actions
kiosk.tileLower area of the kiosk terminal, at most 30 to 40 percent of the heightread from one to two meters awayone stat and a short line, nothing else
court.annotationCourt header in the booking plan and court card on the kiosk, one badge line shared by all appsone badge, up to 24 characters, at most two badges per courta current state of the court: „Flutlicht an“ (floodlight on), „Gesperrt bis 14:00“ (closed until 14:00)
member.list.columnMember list in the admin area, at most two app columnsone table cell, up to 24 characters, title up to 20one status per member: „Offen“ (open), „Bezahlt“ (paid)
member.settings.sectionMember's own settings page, after Verbundene Apps (connected apps)full page widththe member's own settings for your app, connection status
booking.hintBooking dialog, a fixed-height area above the confirm buttonnarrow and short, up to 6 display blocksone sentence about this booking: costs, light, conditions
booking_plan.actionHeader of the booking plan, next to the date navigationone label, up to 24 characters, one icona verb for the plan's day: „Wetter ansehen“ (view weather)
sidebar.actionBottom of the sidebar and the mobile navigation, above Abmelden and Hilfeone label, up to 24 characters, one icona verb for something members open often: „Tür öffnen“ (open door)
nav.pageMain navigation, after the club's own links and before Admin, and a full page of its own (an iframe fills it without a card)one label, up to 24 characters, one icon; the page is full widtha noun for a place members come back to: „Trainingsplan“ (training plan), „Rangliste“ (ladder)

Plan for 320 pixels of width as the smallest case. A row wraps on narrow screens, columns with four to six children wrap to two or three per row, a table with many columns doesn't.

Kiosk: Nobody is signed in at the kiosk, and anyone can tap. Only show what everyone in the clubhouse may see, and don't offer forms that ask for personal details. The tile reloads every five minutes, so per-second values visibly go stale.

Designing declarative UIs

What each block is for

BlockForNot for
headingStructure, when the panel has several parts. level: 2 for sections, 3 below themThe name of your app, or the card title (label) once more
textOne sentence of explanation. tone: "muted" for side notes, hints, empty statesLong instructions, advertising
statThe one important number: „Öffnungen heute“ (openings today), „Offener Betrag“ (amount due). size: "lg" at most once per panelSeveral values of equal weight, use key_value or columns for those
key_valueSeveral details about one object: court, time, statusRunning text
listItems of the same kind: appointments, devices, bookingsNavigation, step-by-step instructions
badgeA status in one or two wordsDecoration, full sentences
buttonAn action without inputNavigating to a website, use link for that
formA short input with a clear purposeLong questionnaires, use a link to your application for those
linkTo your application or help, opens a new tabActions within CheckCourt
dividerSeparating two parts that really have nothing to do with each otherBetween every block
stack / rowGrouping and placing side by side, for example two stat in a rowNesting for its own sake
columnsValues of the same kind in equal widths: forecast days, one stat per courtUnrelated content, more than six values, use list or key_value for those

Layout order

CheckCourt normalises the order of a declarative document, wherever you put buttons and links:

  1. Content first: text, headings, stats, lists, badges, key values, forms, in the order you sent them.
  2. Then one row with all buttons of that level, wrapping on narrow screens.
  3. Then one row with all links of that level, in green with an external link icon.

So links always sit below the content as their own line, and buttons sit above them. The rule applies at the top level of the document and again inside every stack. A row keeps exactly the order you gave it. Consecutive badges share one line.

Write your document in that order anyway, so what you test is what you send. If a button belongs to one part of the panel, put that part and its button in a stack.

Badge variants

variantAppearanceFor
secondary (default)gray, filledneutral states: „Geplant“ (planned), „Inaktiv“ (inactive)
outlineborder onlyextra info without judgment: „Halle“ (indoor), „Vorabversion“ (preview)
defaultfilled in the primary colorthe positive normal case, sparingly: „Verbunden“ (connected), „Bezahlt“ (paid)
warningsoft amberneeds attention but isn't an error: „Regen möglich“ (rain possible), „Platz nass“ (court wet)
destructiveredreal problems only: „Fehlgeschlagen“ (failed), „Überfällig“ (overdue)

The same applies to buttons: outline is the default, default only for the one main action of a panel, destructive only for deleting and disconnecting.

Limits

A document has at most 100 blocks total, counting nested blocks (a stack with three children counts as four), and a nesting depth of at most 3, and texts are plain text without HTML or Markdown. The individual lengths are listed in the block reference. The limits are upper bounds, not targets: a good panel gets by with three to eight blocks.

Do and don't: a dashboard widget

{
  "ui": "v1",
  "blocks": [
    {
      "type": "row",
      "children": [
        { "type": "stat", "label": "Flutlicht heute", "value": "3 Std. 20 Min." },
        { "type": "stat", "label": "Kosten diesen Monat", "value": "48,60 €" }
      ]
    },
    { "type": "text", "text": "Zuletzt eingeschaltet am 06.10.2026 um 19:45 auf Platz 3.", "tone": "muted" }
  ]
}

Two real values (floodlight today, cost this month), one sentence of context (last switched on, date and court), formatted the German way.

{
  "ui": "v1",
  "blocks": [
    { "type": "heading", "text": "⚡ FLUTLICHT-MANAGER PRO", "level": 2 },
    { "type": "badge", "label": "NEU", "variant": "destructive" },
    { "type": "text", "text": "Willkommen! Hier siehst du alle wichtigen Infos rund um dein Flutlicht. Schnell, einfach und smart!" },
    { "type": "divider" },
    { "type": "stat", "label": "Usage", "value": "3.33 h" },
    { "type": "divider" },
    { "type": "button", "label": "Mehr erfahren!", "action_id": "more", "variant": "default" }
  ]
}

App name as the heading, all caps, an emoji, a red „NEU“ (new) with no actual problem, promotional sentences with exclamation marks („Willkommen! … Schnell, einfach und smart!“: welcome, fast, simple and smart), an English label, a number in English format, a button that says nothing („Mehr erfahren!“: learn more).

Do and don't: a booking panel

{
  "ui": "v1",
  "blocks": [
    {
      "type": "key_value",
      "pairs": [
        { "label": "Licht", "value": "Ein bis 21:00" },
        { "label": "Tür", "value": "Offen ab 17:45" }
      ]
    },
    { "type": "button", "label": "Licht verlängern", "action_id": "light.extend" }
  ]
}
{
  "ui": "v1",
  "blocks": [
    { "type": "heading", "text": "Smart Court Control", "level": 2 },
    { "type": "text", "text": "Mit Smart Court Control steuerst du Licht und Tür bequem aus der App. Hier findest du alle Funktionen." },
    { "type": "list", "items": [{ "title": "Licht" }, { "title": "Tür" }] },
    { "type": "button", "label": "Hier klicken", "action_id": "open" }
  ]
}

Describes the app instead of showing the state of the booking. The list (light, door) contains no information, and the button („Hier klicken“: click here) doesn't say what happens.

Host surfaces

Badges, columns and hints sit inside CheckCourt's own screens, next to CheckCourt's own content. They are seen many times a day. Keep them quiet.

Badges on courts

  • Short: one to three words, at most 24 characters. „Flutlicht an“ (floodlight on), not „Das Flutlicht ist auf diesem Platz eingeschaltet“.
  • Prefer secondary or outline. secondary for a state, outline for an extra piece of information. default sparingly.
  • destructive only for real problems that affect play: „Platz gesperrt“ (court closed), „Tür defekt“ (door broken). Not for „Neu“ (new) or for attention.
  • No emoji, no all caps, no app name.
  • Say nothing when there is nothing to say. Leave courts without news out of the list. A badge on every court is noise.

Columns in the member list

  • Short title: one word if possible, at most 20 characters: „Beitrag“ (fee), „Lizenz“ (licence). Use the same text as label in the manifest so the header doesn't change.
  • Terse values: one or two words, at most 24 characters. „Offen“ (open), „Bezahlt“ (paid), „Bis 03/2027“ (until 03/2027). No sentences.
  • Badge or text: set a variant only when the value is a status worth spotting; otherwise plain text reads calmer in a table.
  • Empty is fine. A member without a value gets an empty cell. Don't fill it with „–“, „k. A.“ or „Keine Daten“ (no data).

Hints in the booking dialog

  • One sentence, as a text. A key_value or a link only when it really helps.
  • Inform, don't alarm. The hint never blocks the booking, so don't write it like a warning: „Ab 19:30 brennt das Flutlicht, das kostet 4,50 € pro Stunde.“ (from 19:30 the floodlight is on, at 4.50 € per hour) instead of „ACHTUNG: Flutlichtkosten!“ (warning: floodlight costs!).
  • Only when it matters for this booking. Answer 204 or hidden otherwise. A hint on every booking is soon ignored.

Static action labels

booking_plan.action and sidebar.action show a label and an icon from your manifest.

  • Verb first, at most 24 characters: „Tür öffnen“ (open door), „Wetter ansehen“ (view weather), „Schaden melden“ (report damage). Not your app name, not „Mehr“ (more).
  • Pick an icon that fits the action from the allowed list: door-open for a door, cloud-rain for weather, wrench for damage reports. sparkles is not a default.
  • The dialog continues the label. Open with what the label promised, not with a welcome text.

Designing iframes

An iframe sits in the same card as a declarative panel. The frame itself has a transparent background and no border. So don't set your own background, borders or shadows around the content, and don't add extra outer spacing: the card already provides it.

Exception: nav.page. An iframe page has no card, no title and no padding. The frame fills the content area next to CheckCourt's navigation and scrolls itself. Give it the page padding and the heading yourself, like one of CheckCourt's own pages, and keep the background transparent so --background shows through.

CheckCourt's color values

On load and on every switch between light and dark mode, CheckCourt sends the checkcourt:theme message with these CSS variables. The values below are the current ones, but rely on the message, not on the table.

VariableForLightDark
--backgroundPage background, rarely neededoklch(1 0 0)oklch(0.145 0 0)
--foregroundRegular textoklch(0.145 0 0)oklch(0.985 0 0)
--cardSurface of the card your frame sits inoklch(1 0 0)oklch(0.205 0 0)
--card-foregroundText on --cardoklch(0.145 0 0)oklch(0.985 0 0)
--mutedQuiet surfaces: table headers, empty statesoklch(0.97 0 0)oklch(0.269 0 0)
--muted-foregroundSecondary text, labels, hintsoklch(0.556 0 0)oklch(0.708 0 0)
--primaryMain action, selected stateoklch(0.35 0.1 152) (CheckCourt green)oklch(0.922 0 0) (light)
--primary-foregroundText on --primaryoklch(0.985 0 0)oklch(0.205 0 0)
--secondarySecondary buttonsoklch(0.97 0 0)oklch(0.269 0 0)
--secondary-foregroundText on --secondaryoklch(0.205 0 0)oklch(0.985 0 0)
--accentHover and highlight in listsoklch(0.97 0 0)oklch(0.269 0 0)
--accent-foregroundText on --accentoklch(0.205 0 0)oklch(0.985 0 0)
--destructiveErrors, deleting, overdue itemsoklch(0.577 0.245 27.325)oklch(0.704 0.191 22.216)
--warningWarning text and icons: attention, not an erroroklch(0.5 0.12 58)oklch(0.85 0.15 85)
--warning-foregroundText on a solid --warning surfaceoklch(0.985 0 0)oklch(0.28 0.07 50)
--warning-mutedSoft warning surface behind --warning text, as in warning badgesoklch(0.962 0.059 95.6)oklch(0.3 0.06 70)
--borderLines and bordersoklch(0.922 0 0)oklch(1 0 0 / 10%)
--inputInput field bordersoklch(0.922 0 0)oklch(1 0 0 / 15%)
--ringFocus ringoklch(0.708 0 0)oklch(0.556 0 0)
--radiusBase corner radius0.625rem0.625rem

Green isn't always green

In dark mode, --primary is almost white. If you hard-code CheckCourt green, dark mode gets a color that appears nowhere else there. Always use var(--primary).

CheckCourt derives radii from --radius: small elements like buttons roughly calc(var(--radius) * 0.8), cards and lists var(--radius). No font is passed along. CheckCourt uses Inter. Load it yourself or use the system font, with text at 14 pixels and secondary text at 12 pixels. The frame has an opaque origin, so serve font files with Access-Control-Allow-Origin: *, otherwise the browser blocks them (see iframe extensions).

Applying them

Set light mode defaults so nothing flashes before the first message arrives. The script from UI extensions then overrides them and sets the dark class.

:root {
  --foreground: oklch(0.145 0 0);
  --muted: oklch(0.97 0 0);
  --muted-foreground: oklch(0.556 0 0);
  --primary: oklch(0.35 0.1 152);
  --primary-foreground: oklch(0.985 0 0);
  --destructive: oklch(0.577 0.245 27.325);
  --border: oklch(0.922 0 0);
  --input: oklch(0.922 0 0);
  --ring: oklch(0.708 0 0);
  --radius: 0.625rem;
  color-scheme: light;
}

:root.dark {
  color-scheme: dark;
}

body {
  margin: 0;
  background: transparent;
  color: var(--foreground);
  font: 14px/1.5 Inter, ui-sans-serif, system-ui, sans-serif;
}

.hint {
  color: var(--muted-foreground);
  font-size: 12px;
}

.button {
  min-height: 36px;
  padding: 0 12px;
  border: 1px solid var(--border);
  border-radius: calc(var(--radius) * 0.8);
  background: transparent;
  color: var(--foreground);
}

.button--primary {
  border-color: transparent;
  background: var(--primary);
  color: var(--primary-foreground);
}

.button:focus-visible,
input:focus-visible {
  outline: 3px solid color-mix(in oklch, var(--ring) 50%, transparent);
  outline-offset: 1px;
}

input {
  border: 1px solid var(--input);
  border-radius: calc(var(--radius) * 0.8);
  background: transparent;
  color: var(--foreground);
}

.error {
  color: var(--destructive);
}

@media (prefers-reduced-motion: reduce) {
  * {
    animation: none !important;
    transition: none !important;
  }
}

Test every view in both modes. Images and charts designed on white need their own version for dark mode, or a transparent background.

Behavior in the frame

  • Height: Report it with checkcourt:resize whenever the content changes. No scroll area of your own in the frame, except for really long lists. A nav.page frame is the exception: it always fills the page, ignores resize and scrolls its own document.
  • No popups: No alert(), confirm(), custom modal dialogs over the frame or new windows to confirm something. Confirm inside the frame itself or with checkcourt:toast.
  • Navigation only via checkcourt:navigate: For targets in CheckCourt, send the message instead of linking to CheckCourt URLs. Links to your own application open a new tab (target="_blank").
  • Notifications via checkcourt:toast: Short, one sentence, success for confirmations, error for errors the person can fix themselves.
  • No login page in the frame: Sign the person in via the context token, not with a login form of your own.

Copy

CheckCourt addresses members and board members in German, with the informal du, calm and matter-of-fact. Your copy should sound as if the same team wrote it.

  • German, including labels and error messages. Use jargon only where clubs use it themselves („Flutlicht“ (floodlight), „Medenspiel“ (league match), „Platzwart“ (groundskeeper)).
  • Du, never the formal Sie. For instructions, the infinitive is often enough: „Platz auswählen“ (select court).
  • Sentence case: „Offene Rechnungen“ (open invoices), not „Offene Rechnungen Im Überblick“ (title-cased) and not „OFFENE RECHNUNGEN“.
  • No dashes, meaning no long dashes in the middle of a sentence. Split the sentence, or use a colon or a comma.
  • No exclamation marks and no marketing speak. „Erfolgreich gespeichert!“ (successfully saved!) becomes „Gespeichert“ (saved), „Jetzt smart durchstarten!“ (get smart now!) is dropped.
  • Buttons are verbs: „Licht einschalten“ (turn on lights), „Rechnung senden“ (send invoice), „Verbindung trennen“ (disconnect). Not „OK“, „Weiter“ (next), „Hier klicken“ (click here), „Mehr erfahren“ (learn more).
  • Short: labels at most three or four words. One sentence of explanation is almost always enough.
  • German number and date formats: 07.10.2026, 18:30 in 24-hour format, 1.250,50 €, 3 Std. 20 Min. (3 h 20 min). Weekdays written out or as „Mo“, „Di“ (Mon, Tue).
  • Empty states give the reason: „Noch keine Öffnungen heute.“ (no openings yet today) instead of „Keine Daten“ (no data).
  • Errors say what to do now: „Die Tür antwortet nicht. Versuch es in ein paar Minuten erneut.“ (the door isn't responding, try again in a few minutes) instead of „Error 503“.
Don'tDo
„Super! Deine Buchung wurde erfolgreich synchronisiert!“ (Great! Your booking was successfully synced!)„Buchung übertragen“ (booking transferred)
„STATUS“ above a value„Status“
„Mehr erfahren →“ (learn more)„Rechnung öffnen“ (open invoice)
„Fehler: undefined“ (error: undefined)„Die Daten konnten nicht geladen werden. Versuch es später erneut.“ (the data couldn't be loaded, try again later)
„10/7/2026, 6:30 PM“„07.10.2026, 18:30“

Brand and colors

  • CheckCourt green is the only accent color, specifically as --primary. Don't bring your own brand color, gradients or a second accent color into your extension.
  • No hard-coded color values. No hex, no rgb(), no oklch() in your extension's code, only the variables. That's the only way dark mode works.
  • No per-club look. CheckCourt looks the same in every club. Don't color your extension in club colors, even if you know them.
  • Your logo only as the app icon in the marketplace and under Admin → Apps. Extensions show no app icon, and your content has no place for a logo, wordmark or banner either.
  • Icons sparingly, as line icons in the style of Lucide, in the text color. The easiest way is to use Lucide itself. In declarative blocks, icon takes only the allowed names, and CheckCourt draws them for you. Use an icon where it carries meaning, such as the weather of a forecast day, not in front of every line.

Don't look like a template kit

Signs that a UI was assembled without care stand out in the review:

  • No all-caps headings above sections („ÜBERSICHT“, „STATUS“).
  • No emoji, not in text, headings or badges.
  • No icons in colored boxes as decoration in front of every line.
  • No hairlines between everything. A divider separates parts, not rows.
  • Show the real objects instead of describing them. Instead of „Hier siehst du deine Platzbelegung“ (here you can see your court occupancy), show the occupancy itself. Instead of „3 Geräte verbunden“ (3 devices connected) as a sentence, a list of the three devices and their state.

Accessibility

Declarative extensions come with this built in. For iframes:

  • Contrast through the variables. --foreground on --card and --primary-foreground on --primary are tested. Your own combinations, such as --muted-foreground on --muted for important text, are not.
  • Visible focus for everything clickable, using --ring. Never remove outline without a replacement.
  • Keyboard operable: every action is reachable with Tab and can be triggered with Enter or Space, in a sensible order, without keyboard traps.
  • Real controls: <button> for actions, <a> for links, no <div> with a click handler.
  • Labeled fields: every input has a <label>. A placeholder doesn't replace it.
  • Large enough targets: at least 24 × 24 pixels, 44 × 44 pixels on touch devices.
  • Reduce motion when prefers-reduced-motion is set. Nothing blinks or loops endlessly.
  • No autoplaying media, neither video nor sound.
  • Meaning not by color alone: a red value also needs a word like „Überfällig“ (overdue).

Behavior

  • Fast: Declarative responses have 2 seconds. Keep values cached in your system instead of querying third parties on every request. An iframe shows something useful within one second and loads the rest afterwards.
  • Cache deliberately: CheckCourt reuses a render for 30 seconds unless you say otherwise. Set cache.max_age to 0 for live values such as a door state, and longer (up to 300 seconds) for content that rarely changes.
  • Hide what isn't relevant: If your panel has nothing to say about this booking, member or club, hide it with { "ui": "v1", "hidden": true } or 204 No Content. No card is better than a card that says nothing.
  • Calm empty states where absence is information: If the empty state itself tells the person something, keep the card with a single text with tone: "muted", such as „Heute keine Termine“ (no appointments today) in a widget people check every day, or „Keine offenen Beiträge“ (no open fees) on a profile. An empty document leaves an empty card, so don't send one.
  • Calm errors: If your service can't deliver part of the information, show the rest and one sentence about the missing part. If everything fails, CheckCourt shows „Dieser Inhalt ist gerade nicht verfügbar.“ (this content is currently unavailable), which is better than a half answer with a stack trace.
  • No tracking, no ads: no tracking pixels, no third-party analytics scripts, no advertising, no references to your other products.
  • Only what the person may see: Check the capabilities in the context token. Only show personal details about others when, for example, can_see_booking_details or can_see_member_details is set, and only show controls when the person is allowed to perform the action. Whatever CheckCourt hides from a person, your extension must not show.

The marketplace listing

The fields and their limits are listed in Review and marketplace. For the design:

Icon: Square, at least 128 × 128 pixels, CheckCourt stores it as WebP at 256 × 256 pixels. A simple mark that stays recognizable at 20 pixels. No text, no screenshot, no fine lines. It must work on light and dark backgrounds: use a filled shape with your mark on it, or test a cut-out mark on both backgrounds.

Screenshots: Up to 5, PNG, JPEG or WebP, at most 2 MB each. CheckCourt scales them down to at most 2,000 pixels on the longer side. Show your app where it appears in CheckCourt, with the German UI and in the sandbox club. The first screenshot is the most important: it shows what the app does for the club. Use made-up names, no real members, and no frames, arrows or promotional text in the image.

Name: 2 to 60 characters, the name of your product. No additions like „für CheckCourt“ (for CheckCourt), „Pro“ or „Nr. 1“ (No. 1), and no all caps unless the name is really written that way.

Short description: One sentence of up to 140 characters on what the app does for the club. „Schaltet das Flutlicht passend zu den Buchungen und rechnet den Verbrauch ab.“ (switches the floodlights to match bookings and bills the usage) instead of „Die smarte Lösung für modernes Platzmanagement!“ (the smart solution for modern court management!).

Description: Written for club boards, not for developers. What the app does, what the club needs for it, what it costs. Paragraphs instead of bullet lists full of buzzwords.

Category: The one where a board member would look first.

CategoryFor
Anlage (facilities)Courts, lights, doors, irrigation, maintenance
Finanzen (finance)Fees, invoices, bookkeeping, payments
Spielbetrieb (match play)Teams, tournaments, rankings, training
Kommunikation (communication)Newsletters, messages, calendars, club website
Persönlich (personal)Apps a member connects for themselves

Before you submit

This list follows the support team's review checklist.

Zweck passt zu den Rechten (purpose matches the permissions)

  • Every scope in the manifest is needed by a feature described in the listing.

Datenschutzangaben vollständig, AVV vorhanden, falls nötig (privacy details complete, data processing agreement in place if needed)

  • Data categories, purpose and storage location in the manifest match what the app does.
  • The extensions only show what the person may see according to capabilities.
  • No tracking pixels, no third-party analytics scripts, no advertising.

Im Sandbox-Verein getestet (tested in the sandbox club)

  • Every extension responds in under 2 seconds and either hides itself or has a calm empty state when there's nothing to show.
  • Every extension works in light and dark mode and at 320 pixels of width.
  • Kiosk tiles are readable from two meters away.
  • Court badges, column cells and booking hints were checked with a slow and a failing app: nothing jumps, nothing is shown when the app fails.

Erweiterungen entsprechen den Design- und Textregeln (extensions follow the design and copy rules)

  • Declarative where it's enough. iframes only with a reason.
  • No hard-coded color values, only the variables. No accent color of your own.
  • No app name and no logo in the card content, and nothing that imitates CheckCourt's own controls to mislead.
  • German, du, sentence case, no dashes, no exclamation marks.
  • Buttons are verbs, dates and numbers formatted the German way.
  • No all-caps headings, no emoji, no decorative icons.
  • Badges short and calm, destructive only for real problems. Column values terse, hints one sentence without alarm.
  • Static action labels start with a verb, at most 24 characters, with a fitting icon.
  • In iframes: visible focus, everything keyboard reachable, fields labeled, no popups, navigation only via checkcourt:navigate.

Eintrag (listing)

  • Icon without text, recognizable on light and dark backgrounds.
  • Screenshots from CheckCourt, in German, without real people.
  • Short description in one matter-of-fact sentence.

On this page