Bodenberg.AppDimens.Maui.Sdps

May 23, 2026 · View on GitHub

Fully native .NET MAUI responsive sizing library with architectural, functional, and ergonomic parity with AppDimens SDP Android v3.1.5.

It brings the Android experience of @dimen/_16sdp, orientation inverters, discrete buckets, aspect ratio, and conditional builders to MAUI — with no JNI bindings, AARs, or embedded Android code.


Table of contents


What the library does

CapabilityDescription
Scalable dimensionssdp, hdp, wdp, ssp, and variants with aspect ratio (*a) or without font scale (sem/hem/wem)
Android buckets~358 screen sizes (30…5120 dp) precomputed on three axes (sw, w, h)
Scaling modesContinuous, bucket, hybrid (default)
Inverters8 inverters + Default to swap axis by orientation
BuildersResponsive.Value() / ScaledDp() with tablet, landscape, desktop rules and advanced Screen()
HelpersSdpRotate, SdpQualifier, SdpMode, SdpScreen
Dual XAMLShort syntax {sdp:16} and normal {dimen:Sdp Value=16}
Fluent C#16.Sdp(), PaddingSdp(), FontSsp()
BindingIValueConverter for {Binding}
ReactivityResolver refreshes metrics on rotation/resize; XAML markup is load-time (see Screen rotation and resize)

Requirements

RequirementVersion
.NET8, 9, or 10
.NET MAUI8.0+ / 9.0+ / 10.0+ (per SDK)
PlatformsAndroid, iOS, macOS, Windows (via MAUI)
NuGet version3.5.2

Installation

dotnet add package Bodenberg.AppDimens.Maui.Sdps --version 3.5.2

Project reference (local development):

<ProjectReference Include="path/to/AppDimens.Maui.Sdk/AppDimens.Maui.Sdk.csproj" />

Quick start

1. Register in MauiProgram.cs

Required — initializes metrics, buckets, and MainDisplayInfoChanged listener:

using AppDimens.Maui;
using AppDimens.Maui.Core;

public static MauiApp CreateMauiApp()
{
    var builder = MauiApp.CreateBuilder();
    builder.UseAppDimensSdps(options =>
    {
        options.ScalingMode = ScalingMode.Hybrid;   // recommended
        options.WarmupAspectRatio = true;
        options.DefaultFontScale = 1.0f;
    });
    // ... rest of app
    return builder.Build();
}

Alternative without MauiAppBuilder:

AppDimensSdps.Initialize(new AppDimensOptions { ScalingMode = ScalingMode.Hybrid });

2. Declare XAML namespaces

See XAML namespaces for the full list. Minimum:

xmlns:sdp="clr-namespace:AppDimens.Maui.Markup;assembly=AppDimens.Maui.Markup"
xmlns:ssp="clr-namespace:AppDimens.Maui.Markup;assembly=AppDimens.Maui.Markup"
xmlns:dimen="clr-namespace:AppDimens.Maui.Xaml;assembly=AppDimens.Maui.Xaml"

3. Use dimensions

<VerticalStackLayout Padding="{sdp:16}" Spacing="{sdp:8}">
    <Label Text="Title" FontSize="{ssp:20}" />
    <Label Text="Responsive" FontSize="{scaled:14, Tablet=18, Landscape=16}" />
</VerticalStackLayout>
using AppDimens.Maui.Extensions;

double padding = 16.Sdp();
double fontSize = 18.Ssp();
myLabel.FontSsp(20);
myLayout.PaddingSdp(16);

4. Screen rotation

Initialize once in step 1 — no extra AppDimens config. On Android, keep ConfigurationChanges on MainActivity (MAUI default). XAML markup is evaluated at page load; see Screen rotation and resize for what updates automatically vs what you may need to refresh in code.


Screen rotation and resize

AppDimens does not use Android -land / -port resource qualifiers. Rotation is handled at runtime by recalculating screen metrics, re-selecting buckets per axis, and (optionally) axis inverters and conditional helpers.

What you must do (once)

StepRequired?Where
Call UseAppDimensSdps() / AppDimensSdps.Initialize()YesMauiProgram.cs — registers DeviceDisplay.MainDisplayInfoChanged and loads buckets
Android ConfigurationChanges on MainActivityRecommendedPlatforms/Android/MainActivity.cs — keeps the process alive on rotation (MAUI default)
Android OnConfigurationChangedRefreshMetricsFromDevice()RecommendedSame file — ensures metrics/cache refresh when the activity is not recreated (see sample)
Declare anything in AndroidManifest.xml for rotationNoRotation is allowed by default; lock orientation only if your app requires it
Extra AppDimens config file / handler classNoMetrics refresh is built into the resolver

Android MainActivity — keep (or add) configuration change handling so rotation does not tear down the activity unexpectedly:

[Activity(
    Theme = "@style/Maui.SplashTheme",
    MainLauncher = true,
    ConfigurationChanges = ConfigChanges.ScreenSize
        | ConfigChanges.Orientation
        | ConfigChanges.UiMode
        | ConfigChanges.ScreenLayout
        | ConfigChanges.SmallestScreenSize
        | ConfigChanges.Density)]
public class MainActivity : MauiAppCompatActivity { }

This matches the MAUI template and the sample app. iOS, Mac Catalyst, and Windows do not need an equivalent declaration.

Android — refresh metrics when configuration changes (included in the sample app):

public override void OnConfigurationChanged(Configuration newConfig)
{
    base.OnConfigurationChanged(newConfig);
    AppDimensResolver.Instance.RefreshMetricsFromDevice();
}

With ConfigurationChanges, the activity may stay alive without recreating the page. This call guarantees widthDp / heightDp / cache invalidation even if MainDisplayInfoChanged is delayed or skipped on some devices.

What the library updates automatically

After initialization, on rotation, resize, DPI change, or multi-window:

  1. AppDimensResolver reads new values from DeviceDisplay.MainDisplayInfo
  2. Updates widthDp, heightDp, smallestDp, and Orientation in Metrics.Current
  3. Invalidates the dimension cache and responsive/bucket state (always when metrics change, all platforms)
  4. New calls to 16.Sdp(), converters, and resolver APIs use the updated metrics

You do not need to call RefreshMetricsFromDevice() manually on iOS, Mac Catalyst, or Windows for normal rotation. On Android with ConfigurationChanges, calling it from OnConfigurationChanged (as in the sample) is recommended. Use manual refresh only for diagnostics, tests, or a “refresh metrics” button.

XAML markup vs rotation (important)

ApproachUpdates on rotation?
{sdp:16}, {dimen:Sdp Value=16}, {sdpPh:16}, {scaled:…}, {sdpRotate:…}Only when the page/layout is built again — markup runs at load time
16.Sdp() in C# (e.g. OnAppearing, SizeChanged, button handlers)Yes — always uses current metrics
{Binding … Converter={StaticResource SdpConv}}Yes — if the binding re-evaluates when you refresh the source or layout

With ConfigurationChanges (recommended on Android), the activity stays alive and XAML-set properties (Padding, WidthRequest, FontSize, …) keep their previous pixel values until you update them.

Practical guidance:

  • Most screens: no extra work — dimensions are resolved once; small drift on rotate is often acceptable, or the user navigates away and back (OnAppearing rebuilds labels you set in code).
  • Layouts that must track rotation: update sizes in code when the page resizes, or re-apply values in OnAppearing / SizeChanged:
protected override void OnSizeAllocated(double width, double height)
{
    base.OnSizeAllocated(width, height);
    MyLayout.Padding = new Thickness(16.Sdp());
    TitleLabel.FontSize = 20.Ssp();
}
  • Sample app pattern: subscribe to AppDimensResolver.Instance.Metrics.Changed while the page is visible and re-run your C# refresh (see DimensSampleRefresh.WhenMetricsChange in samples/AppDimens.Maui.Sample/Services/DimensSampleRefresh.cs). All demo tabs (Home, Dimensions, Inverters, Advanced, Benchmark) use this helper.

  • Orientation-specific values: use {scaled:14, Landscape=16}, {sdpRotate:30, Rotation=45, Orientation=Landscape}, or inverters ({sdpPh:16}, {wdpLh:120}) — the chosen branch depends on orientation at resolve time (typically page load; re-apply in code after rotation if the UI must update live).

Inverters on rotation

Inverters (sdpPh, sdpLw, hdpPw, …) choose width/height/smallest axis from current orientation at resolve time. After rotation, the next 16.SdpPh() (or a refreshed XAML property) uses the new orientation. No separate inverter registration is required.


How it works

Dimension types

TypeAxis / baseTypical use
sdpsmallestWidth (shorter screen side)padding, margin, general sizing
hdpscreen heightheights tied to the vertical axis
wdpscreen widthwidths tied to the horizontal axis
sspsame index as sdp + system font scaleFontSize with accessibility
sem / hem / wemlike ssp/hsp/wsp, without font scalefixed typography
sdpa / hdpa / wdpadimension + aspect ratio adjustmentnon-16:9 screens

Formulas

Continuous mode (runtime):

$ \text{value} = (\text{index} / 300) \times \text{axisMetric} $

  • sdp → metric = min(widthDp, heightDp)
  • hdp → metric = heightDp
  • wdp → metric = widthDp

Bucket mode (discrete, Android parity):

$ \text{value} = \text{index} \times (\text{bucketDp} / 300) $

Precomputed buckets for ~358 sizes (30…5120, step 15 + real devices: 384, 411, 640, etc.) on three independent axes: sw, w, h.

Aspect ratio (*a):

After resolving the base dimension, applies a multiplier derived from the current aspect ratio vs. the 1.78 reference (formula identical to Android AppDimensSdpsFactors).

Index ranges

TypeRangeEquivalent Android key
sdp / hdp / wdp / *a-300 … 600_16sdp, _minus8sdp, _100wdp
ssp / sem / hem / wem1 … 600_18ssp (no negatives)

Negative indexes are useful for inverted offsets and margins (e.g. -8_minus8sdp).

Qualifier system (buckets)

Equivalent to the Android merge values-sw{N}dp, values-w{N}dp, values-h{N}dp:

  1. For each axis, selects the largest bucket N where N ≤ current metric
  2. Loads precomputed values from that bucket
  3. Falls back to base (scale = 1.0) when needed

There are no -land or -port qualifiers (Android SDP parity). Rotation is handled by:

  • Recalculating widthDp, heightDp, smallestDp on DeviceDisplay.MainDisplayInfoChanged
  • Re-selecting the bucket per axis (sw / w / h)
  • Inverters (sdpPh, sdpLw, …) that swap axis by orientation
  • Conditional APIs (SdpRotate, Responsive.Value().Landscape(), builders)

Scaling modes (ScalingMode)

ModeBehavior
Hybrid (default)Uses bucket if the key exists; otherwise continuous formula
ContinuousAlways the runtime math formula
BucketAlways generated table/bucket lookup
HybridPreferContinuousAlways continuous (bucket ignored)

If the Generated bucket folder is not found at runtime, Hybrid and Bucket automatically fall back to the continuous formula.

Reacting to screen changes

The resolver automatically invalidates cache and refreshes metrics on:

  • Rotation
  • Resize (desktop, split-screen, foldables)
  • DPI / density change
  • Multi-window

Event: DeviceDisplay.MainDisplayInfoChanged (subscribed in AppDimensSdps.Initialize).

UI note: XAML markup values are applied at page load (see Screen rotation and resize). C# calls and bindings re-read current metrics whenever you invoke them.


Pre-generated resources (Generated/)

This package ships thousands of pre-calculated dimension entries under AppDimens.Maui.Resources/Generated/. That is intentional: MAUI SDP mirrors how AppDimens SDP on Android works, where sizes come from discrete resource buckets (values-sw{N}dp, values-w{N}dp, values-h{N}dp), not from one formula applied to every key at build time.

The tables are produced by scripts/generate-dimens.py (same role as the Android generator-sdps.py pipeline) and packaged with the NuGet as contentFiles/.../Generated/.

What is in Generated/

ArtifactRole
buckets.jsonBucket list (layoutVersion: 2) — ≈358 sizes from 30…5120 dp, step 15 + common devices
Dimens.Base.xamlBase scale (1.0) — axis-neutral keys _1_600, _minus1
Dimens.{N}.xamlOne dictionary per bucket N with keys _index / _minusindex (values scaled by index × (N / 300)); axis (sw/w/h) is chosen at runtime, not duplicated in XML
Runtime loadBucketRegistry.LoadFromGenerated() parses these files when AppDimensSdps.Initialize(..., generatedPath) finds the folder

At runtime the resolver picks the largest bucket N where N ≤ current metric on each axis (sw, w, h) — the same qualifier merge idea as Android — and reads the precomputed value for the axis-neutral key _16 (not separate _16sdp / _16wdp / _16hdp entries).

What the pre-generated data is for

  1. Bucket and Hybrid modes (default) — return the same discrete values as the Android @dimen tables for the active bucket, instead of only the continuous approximation index / 300 × metric.
  2. Faithful porting — layouts migrated from Android SDP can match pixel results when bucket mode is active.
  3. Performance on the hot path — after the first resolve, DimensionCache stores the result; bucket mode resolves via parsed dictionaries (see Performance and caching).
  4. Aspect ratio (sdpa, hdpa, wdpa)AspectRatioFactors uses _1 from the active bucket per axis when buckets are loaded (GetOneUnit), aligned with Android’s bucket-based AR behavior.

You can ignore the files if you want

  • Set ScalingMode.Continuous or ScalingMode.HybridPreferContinuous — only the runtime formula is used; no bucket lookup.
  • If Generated/ is missing at runtime, Hybrid and Bucket fall back automatically to the continuous formula (see Scaling modes).

The library still includes the data so hybrid/bucket parity with Android SDP is available by default without regenerating tables in your app.

Prefer runtime-only scaling (no pre-generated grids)?

If you do not want bundled bucket/XML-style dimension tables and prefer code-driven strategies computed at runtime only, use AppDimens Dynamic:

This package (MAUI SDP)AppDimens Dynamic
Monorepo folderappdimens-sdps-net/appdimens-dynamic-net-binding/
NuGetBodenberg.AppDimens.Maui.SdpsBodenberg.AppDimens.Dynamic
ModelSDP/HDP/WDP + SSP and Android-style buckets15 scaling strategies (percent, auto, fluid, …), facilitators, builders — no pre-built @dimen grids
PlatformsNative .NET MAUI (Android, iOS, Windows, macOS).NET for Android binding (usable from MAUI’s Android head; not a cross-platform MAUI-native port)

See the Dynamic binding README and the monorepo overview.


Configuration

AppDimensOptions

PropertyTypeDefaultDescription
ScalingModeScalingModeHybridCalculation mode (see table above)
WarmupAspectRatiobooltruePrecomputes AR factors on startup (Warmup())
DefaultFontScalefloat1.0Initial font scale for ssp/sem

Manual initialization (optional)

using AppDimens.Maui;
using AppDimens.Maui.Core;

AppDimensSdps.Initialize(new AppDimensOptions
{
    ScalingMode = ScalingMode.Bucket,
    WarmupAspectRatio = true,
});

AppDimensSdps.Warmup(); // optional, forces immediate warmup

Custom bucket path (advanced)

AppDimensSdps.Initialize(
    options: new AppDimensOptions { ScalingMode = ScalingMode.Bucket },
    generatedPath: "/path/to/AppDimens.Maui.Resources/Generated");

Useful in tests or when generated files live outside the default location.

System font scale

ssp applies the configured font scale. To reflect the user’s accessibility preference:

AppDimensResolver.Instance.SetFontScale(/* system value */);

XAML namespaces

Each short markup extension uses a prefix equal to the extension name (no Extension suffix). They all target the same assembly AppDimens.Maui.Markup.

Recommended: create Resources/Styles/AppDimensImports.xaml and merge it in App.xaml:

<?xml version="1.0" encoding="UTF-8" ?>
<ResourceDictionary
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:sdp="clr-namespace:AppDimens.Maui.Markup;assembly=AppDimens.Maui.Markup"
    xmlns:ssp="clr-namespace:AppDimens.Maui.Markup;assembly=AppDimens.Maui.Markup"
    xmlns:hsp="clr-namespace:AppDimens.Maui.Markup;assembly=AppDimens.Maui.Markup"
    xmlns:wsp="clr-namespace:AppDimens.Maui.Markup;assembly=AppDimens.Maui.Markup"
    xmlns:sspa="clr-namespace:AppDimens.Maui.Markup;assembly=AppDimens.Maui.Markup"
    xmlns:hdp="clr-namespace:AppDimens.Maui.Markup;assembly=AppDimens.Maui.Markup"
    xmlns:wdp="clr-namespace:AppDimens.Maui.Markup;assembly=AppDimens.Maui.Markup"
    xmlns:sdpa="clr-namespace:AppDimens.Maui.Markup;assembly=AppDimens.Maui.Markup"
    xmlns:hdpa="clr-namespace:AppDimens.Maui.Markup;assembly=AppDimens.Maui.Markup"
    xmlns:wdpa="clr-namespace:AppDimens.Maui.Markup;assembly=AppDimens.Maui.Markup"
    xmlns:sem="clr-namespace:AppDimens.Maui.Markup;assembly=AppDimens.Maui.Markup"
    xmlns:hem="clr-namespace:AppDimens.Maui.Markup;assembly=AppDimens.Maui.Markup"
    xmlns:wem="clr-namespace:AppDimens.Maui.Markup;assembly=AppDimens.Maui.Markup"
    xmlns:sdpPh="clr-namespace:AppDimens.Maui.Markup;assembly=AppDimens.Maui.Markup"
    xmlns:sdpLw="clr-namespace:AppDimens.Maui.Markup;assembly=AppDimens.Maui.Markup"
    xmlns:sdpLh="clr-namespace:AppDimens.Maui.Markup;assembly=AppDimens.Maui.Markup"
    xmlns:sdpPw="clr-namespace:AppDimens.Maui.Markup;assembly=AppDimens.Maui.Markup"
    xmlns:hdpLw="clr-namespace:AppDimens.Maui.Markup;assembly=AppDimens.Maui.Markup"
    xmlns:hdpPw="clr-namespace:AppDimens.Maui.Markup;assembly=AppDimens.Maui.Markup"
    xmlns:wdpLh="clr-namespace:AppDimens.Maui.Markup;assembly=AppDimens.Maui.Markup"
    xmlns:wdpPh="clr-namespace:AppDimens.Maui.Markup;assembly=AppDimens.Maui.Markup"
    xmlns:sdpPhA="clr-namespace:AppDimens.Maui.Markup;assembly=AppDimens.Maui.Markup"
    xmlns:sspPh="clr-namespace:AppDimens.Maui.Markup;assembly=AppDimens.Maui.Markup"
    xmlns:sspPhA="clr-namespace:AppDimens.Maui.Markup;assembly=AppDimens.Maui.Markup"
    xmlns:scaled="clr-namespace:AppDimens.Maui.Markup;assembly=AppDimens.Maui.Markup"
    xmlns:sdpRotate="clr-namespace:AppDimens.Maui.Markup;assembly=AppDimens.Maui.Markup"
    xmlns:sspRotate="clr-namespace:AppDimens.Maui.Markup;assembly=AppDimens.Maui.Markup"
    xmlns:dimen="clr-namespace:AppDimens.Maui.Xaml;assembly=AppDimens.Maui.Xaml" />

In App.xaml:

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Resources/Styles/AppDimensImports.xaml" />
            <!-- your other dictionaries -->
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

Two coexisting syntaxes

FormPatternExample
Short{prefix:value}{sdp:16}
Normal{dimen:Type …}{dimen:Sdp Value=16}

Both produce the same double at runtime. The normal syntax accepts Inverter= on all base types.


XAML usage — complete reference

Base dimensions — short syntax

MarkupAndroidTypical MAUI property
{sdp:16}@dimen/_16sdpPadding, HeightRequest, WidthRequest, Spacing, CornerRadius
{sdp:-8}@dimen/_minus8sdpnegative offsets
{ssp:18}scaled spFontSize
{hdp:48}@dimen/_48hdpheights on the height axis
{wdp:120}@dimen/_120wdpwidths on the width axis
{sdpa:16}16.sdpadimension + aspect ratio
{hdpa:16}16.hdpahdp + aspect ratio
{wdpa:16}16.wdpawdp + aspect ratio
{sem:18}18.semfont without font scale
{hem:18}18.hemhem without font scale
{wem:18}18.wemwem without font scale
{hsp:18}18.hspfont on height axis + font scale
{wsp:18}18.wspfont on width axis + font scale
{sspa:18}18.sspassp + aspect ratio
{hspa:18}18.hspahsp + aspect ratio
{wspa:18}18.wspawsp + aspect ratio
{sema:18}18.semasem + aspect ratio
{hema:18}18.hemahem + aspect ratio
{wema:18}18.wemawem + aspect ratio

Base dimensions — normal syntax

MarkupDescription
{dimen:Sdp Value=16}SDP smallest width
{dimen:Ssp Value=18}SSP with font scale
{dimen:Hdp Value=48}HDP
{dimen:Wdp Value=120}WDP
{dimen:Sdpa Value=16}SDPA
{dimen:Hdpa Value=16}HDPA
{dimen:Wdpa Value=16}WDPA
{dimen:Sem Value=18}SEM
{dimen:Hem Value=18}HEM
{dimen:Wem Value=18}WEM
{dimen:Hsp Value=18}HSP
{dimen:Wsp Value=18}WSP
{dimen:Sspa Value=18}SSPA
{dimen:Hspa Value=18}HSPA
{dimen:Wspa Value=18}WSPA
{dimen:Sema Value=18}SEMA
{dimen:Hema Value=18}HEMA
{dimen:Wema Value=18}WEMA

Optional property on all base dimen:* types:

{dimen:Sdp Value=16 Inverter=SwToPh}

Inverter values: Default, PhToLw, PwToLh, LhToPw, LwToPh, SwToLh, SwToLw, SwToPh, SwToPw.

Inverter shortcuts — short syntax

Short markupAndroidInternal inverter
{sdpPh:16}16.sdpPhSwToPh
{sdpLw:16}16.sdpLwSwToLw
{sdpLh:16}16.sdpLhSwToLh
{sdpPw:16}16.sdpPwSwToPw
{hdpLw:12}12.hdpLwPhToLw
{hdpPw:12}12.hdpPwPwToLh
{wdpLh:12}12.wdpLhPwToLh
{wdpPh:12}12.wdpPhPhToLw
{sdpPhA:16}16.sdpPhaSwToPh + aspect ratio
{sdpLwA:16}16.sdpLwaSwToLw + aspect ratio
{sdpLhA:16}16.sdpLhaSwToLh + aspect ratio
{sdpPwA:16}16.sdpPwaSwToPw + aspect ratio

Inverter shortcuts — typography (ssp / hsp / wsp / sem / hem / wem)

Short markupAndroidInverter
{sspPh:18}18.sspPhSwToPh
{sspLw:18}18.sspLwSwToLw
{sspLh:18}18.sspLhSwToLh
{sspPw:18}18.sspPwSwToPw
{hspLw:18}18.hspLwPhToLw
{hspPw:18}18.hspPwLhToPw
{wspLh:18}18.wspLhPwToLh
{wspPh:18}18.wspPhLwToPh
{semPh:18}18.semPhSwToPh
{semLw:18}18.semLwSwToLw
{semLh:18}18.semLhSwToLh
{semPw:18}18.semPwSwToPw
{hemLw:18}18.hemLwPhToLw
{hemPw:18}18.hemPwLhToPw
{wemLh:18}18.wemLhPwToLh
{wemPh:18}18.wemPhLwToPh

Inverter + aspect ratio shortcuts — typography

Short markupAndroid
{sspPhA:18}18.sspPha
{sspLwA:18}18.sspLwa
{sspLhA:18}18.sspLha
{sspPwA:18}18.sspPwa
{hspLwA:18}18.hspLwa
{hspPwA:18}18.hspPwa
{wspLhA:18}18.wspLha
{wspPhA:18}18.wspPha
{semaPh:18}18.semaPh
{semaLw:18}18.semaLw
{semaLh:18}18.semaLh
{semaPw:18}18.semaPw
{hemaLw:18}18.hemaLw
{hemaPw:18}18.hemaPw
{wemaLh:18}18.wemaLh
{wemaPh:18}18.wemaPh

Normal-syntax equivalent (example):

<Label FontSize="{dimen:Sspa Value=18 Inverter=SwToPh}" />
<Label FontSize="{dimen:Sema Value=18 Inverter=SwToLw}" />
<Label FontSize="{dimen:Sdpa Value=16 Inverter=SwToLw}" />

Inverter shortcuts on hdpa/wdpa/hspa/wspa use normal syntax only ({dimen:Hdpa Value=16 Inverter=SwToLw}) — there are no dedicated short tokens for those axes.

Inverter shortcuts — normal syntax

<Label Padding="{dimen:Sdp Value=16 Inverter=SwToPh}" />
<Label Padding="{dimen:Sdp Value=16 Inverter=SwToLw}" />
<Label Padding="{dimen:Hdp Value=12 Inverter=PhToLw}" />
<Label Padding="{dimen:Wdp Value=12 Inverter=PwToLh}" />
<Label Padding="{dimen:Sdpa Value=16 Inverter=SwToPh}" />

Conditional dimensions (responsive markup)

Scaled builder — shortcuts for tablet, landscape, and desktop:

<!-- Short: ContentProperty = Value -->
<Label FontSize="{scaled:14, Tablet=18, Landscape=16, Desktop=20}" />

<!-- Normal -->
<Label FontSize="{dimen:Scaled Value=14 Tablet=18 Landscape=16 Desktop=20}" />
PropertyCondition
TabletsmallestWidth ≥ 600
Landscapelandscape orientation
DesktopUiModeType.Desk

Rotate — alternate value by orientation (all 9 axes):

<!-- DP — short uses ContentProperty Base -->
<Label HeightRequest="{sdpRotate:30, Rotation=45, Orientation=Landscape}" />
<Label HeightRequest="{hdpRotate:48, Rotation=64, Orientation=Landscape}" />
<Label HeightRequest="{wdpRotate:120, Rotation=160, Orientation=Portrait}" />

<!-- DP — normal -->
<Label HeightRequest="{dimen:SdpRotate Base=30, Rotation=45, Orientation=Landscape}" />
<Label HeightRequest="{dimen:HdpRotate Base=48, Rotation=64, Orientation=Landscape}" />
<Label HeightRequest="{dimen:WdpRotate Base=120, Rotation=160, Orientation=Portrait}" />

<!-- SP / SEM — short -->
<Label FontSize="{sspRotate:16, Rotation=20, Orientation=Landscape}" />
<Label FontSize="{hspRotate:16, Rotation=20}" />
<Label FontSize="{wspRotate:16, Rotation=20}" />
<Label FontSize="{semRotate:16, Rotation=20}" />
<Label FontSize="{hemRotate:16, Rotation=20}" />
<Label FontSize="{wemRotate:16, Rotation=20}" />

<!-- SP / SEM — normal -->
<Label FontSize="{dimen:SspRotate Base=16, Rotation=20, Orientation=Landscape}" />
<Label FontSize="{dimen:SemRotate Base=16, Rotation=20, Orientation=Landscape}" />
<Label FontSize="{dimen:HemRotate Base=16, Rotation=20, Orientation=Landscape}" />
<Label FontSize="{dimen:WemRotate Base=16, Rotation=20, Orientation=Landscape}" />
PropertyDescription
BaseValue when orientation does not match
RotationValue when orientation matches
OrientationPortrait or Landscape (default: Landscape)

Mode — alternate value by UiModeType:

<Label FontSize="{sdpMode:16, Mode=24, UiMode=Desk}" />
<Label FontSize="{dimen:SdpMode Base=16, Mode=24, UiMode=Desk}" />
<Label FontSize="{sspMode:18, Mode=26, UiMode=Television}" />
<Label FontSize="{dimen:SspMode Base=18, Mode=26, UiMode=Television}" />

Available short tokens: sdpMode, sspMode, hspMode, wspMode, semMode, hemMode, wemMode (and matching dimen:*Mode).

Qualifier — alternate value when a screen metric crosses a threshold:

<Label FontSize="{sdpQualifier:16, Qualified=20, QualifierType=SmallWidth, Threshold=600}" />
<Label FontSize="{dimen:SdpQualifier Base=16, Qualified=20, QualifierType=SmallWidth, Threshold=600}" />

Available short tokens: sdpQualifier, sspQualifier, hspQualifier, wspQualifier, semQualifier, hemQualifier, wemQualifier.

Screen — alternate value when both UiMode and qualifier threshold match:

<Label FontSize="{sdpScreen:16, Screen=20, UiMode=Television, QualifierType=SmallWidth, Threshold=600}" />
<Label FontSize="{dimen:SdpScreen Base=16, Screen=20, UiMode=Television, QualifierType=SmallWidth, Threshold=600}" />

Available short tokens: sdpScreen, sspScreen, hspScreen, wspScreen, semScreen, hemScreen, wemScreen.

Optional FinalQualifier on Mode / Qualifier / Screen selects the axis used to resolve the alternate value (same defaults as C#).

C# only: chained Responsive.Value(12).Screen(UiModeType.Television, …) beyond {scaled:…} shortcuts; physical pixels (SdpPx(), etc.); binding converters.

Android resource keys (via code)

On Android, @dimen/_16sdp references an XML resource. In MAUI there is no XAML ResourceDictionary with _16sdp for {StaticResource} — buckets are consumed internally by the resolver.

MAUI equivalents:

AndroidMAUI (recommended)
@dimen/_16sdp{sdp:16} or {dimen:Sdp Value=16}
@dimen/_16sdp in Kotlin16.Sdp() or AppDimensSdps.Sdp(16)
@dimen/_minus8sdp{sdp:-8} or (-8).Sdp()
@dimen/_100wdp{wdp:100} or 100.Wdp()

Markups resolve to pixels at page load (like a one-shot {StaticResource}). The resolver keeps metrics up to date on rotation; to refresh on-screen sizes, update properties in code or reload the page — see Screen rotation and resize.

Full page example

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:sdp="clr-namespace:AppDimens.Maui.Markup;assembly=AppDimens.Maui.Markup"
    xmlns:ssp="clr-namespace:AppDimens.Maui.Markup;assembly=AppDimens.Maui.Markup"
    xmlns:scaled="clr-namespace:AppDimens.Maui.Markup;assembly=AppDimens.Maui.Markup"
    xmlns:dimen="clr-namespace:AppDimens.Maui.Xaml;assembly=AppDimens.Maui.Xaml"
    x:Class="MyApp.MainPage">

    <ScrollView Padding="{sdp:16}">
        <VerticalStackLayout Spacing="{sdp:12}">

            <Label Text="AppDimens MAUI"
                   FontSize="{ssp:22}"
                   FontAttributes="Bold" />

            <Label Text="Portrait→height inverter"
                   Padding="{sdpPh:12}"
                   FontSize="{ssp:14}" />

            <Label Text="Tablet / Landscape"
                   FontSize="{scaled:14, Tablet=18, Landscape=16}" />

            <BoxView Color="Gray"
                     HeightRequest="{dimen:Hdp Value=48}"
                     CornerRadius="{sdp:8}" />

            <Label Text="Negative offset"
                   Margin="{sdp:-4}" />

            <Label Text="Explicit equivalent"
                   Padding="{dimen:Sdp Value=16 Inverter=Default}" />

        </VerticalStackLayout>
    </ScrollView>
</ContentPage>

C# usage — complete reference

int extensions — base dimensions

using AppDimens.Maui.Extensions;

int index = 16;

double a = index.Sdp();    // smallest width
double b = index.Ssp();    // sp + font scale
double c = index.Hdp();    // height
double d = index.Wdp();    // width
double e = index.Sdpa();   // sdp + aspect ratio
double f = index.Hdpa();   // hdp + aspect ratio
double g = index.Wdpa();   // wdp + aspect ratio
double h = index.Sem();    // sp without font scale
double i = index.Hem();
double j = index.Wem();

// Typography per axis + font scale
double k = index.Hsp();
double l = index.Wsp();

// Typography + aspect ratio
double m = index.Sspa();
double n = index.Hspa();
double o = index.Wspa();
double p = index.Sema();   // sem + aspect ratio
double q = index.Hema();
double r = index.Wema();

// Negatives (sdp/hdp/wdp only)
double offset = (-8).Sdp();

int extensions — inverters (DP)

double ph  = 16.SdpPh();   // SwToPh
double lw  = 16.SdpLw();   // SwToLw
double lh  = 16.SdpLh();   // SwToLh
double pw  = 16.SdpPw();   // SwToPw
double hlw = 12.HdpLw();
double hpw = 12.HdpPw();
double wlh = 12.WdpLh();
double wph = 12.WdpPh();

// DP + aspect ratio
double pha = 16.SdpPha();
double lwa = 16.SdpLwa();
double lha = 16.SdpLha();
double pwa = 16.SdpPwa();

int extensions — inverters (SP)

// SSP (smallest width + font scale)
18.SspPh(); 18.SspLw(); 18.SspLh(); 18.SspPw();
18.SspPha(); 18.SspLwa(); 18.SspLha(); 18.SspPwa();

// HSP / WSP
18.HspLw(); 18.HspPw();
18.WspLh(); 18.WspPh();
18.HspLwa(); 18.HspPwa();
18.WspLha(); 18.WspPha();

// SEM / HEM / WEM (no font scale)
18.SemPh(); 18.SemLw(); 18.SemLh(); 18.SemPw();
18.HemLw(); 18.HemPw();
18.WemLh(); 18.WemPh();
18.SemaPh(); 18.SemaLw(); 18.SemaLh(); 18.SemaPw();
18.HemaLw(); 18.HemaPw();
18.WemaLh(); 18.WemaPh();

int extensions — physical pixels

double pxSdp = 16.SdpPx();  // sdp × density
double pxSsp = 18.SspPx();
double pxHsp = 18.HspPx();
double pxWsp = 18.WspPx();
double pxSem = 18.SemPx();

MAUI control extensions

using AppDimens.Maui.Extensions;

// Uniform padding on Layout or Border
myStack.PaddingSdp(16);
myBorder.PaddingSdp(12);

// Uniform margin on any View
myLabel.MarginSdp(8);

// FontSize on Label
myLabel.FontSsp(20);
myLabel.FontHsp(20);
myLabel.FontWsp(20);
myLabel.FontSem(20);

For asymmetric padding/margin, compute each side:

var h = 16.Sdp();
var v = 8.Sdp();
myStack.Padding = new Thickness(h, v);

AppDimensResolver — advanced API

using AppDimens.Maui;
using AppDimens.Maui.Core;
using AppDimens.Maui.Inverters;

var r = AppDimensResolver.Instance;

// Typed shortcuts
r.Sdp(16);
r.Ssp(18);
r.Hsp(18);
r.Wsp(18);
r.Hdp(48);
r.Wdp(120);
r.Sdpa(16);
r.Sspa(18);
r.Hspa(18);
r.Wspa(18);
r.Sem(18);
r.Sema(18);
r.Hem(18);
r.Hema(18);
r.Wem(18);
r.Wema(18);

// With inverter
r.Sdp(16, InverterType.SwToPh);
r.Ssp(18, InverterType.SwToPh);
r.Sspa(18, InverterType.SwToLw);
r.Sem(18, InverterType.SwToLw);

// Generic resolve
r.Resolve(
    index: 16,
    baseQualifier: DpQualifier.SmallWidth,
    inverter: InverterType.Default,
    applyAspectRatio: false,
    applyFontScale: false,
    allowNegative: true);

// Active bucket (Bucket/Hybrid mode)
var bucketSw = r.Buckets?.GetActiveBucket(DpQualifier.SmallWidth);

// Font scale
r.SetFontScale(1.15f);

Orientation inverters

Inverters swap the resource axis (sw / h / w) by orientation — equivalent to the Android Inverter enum.

InverterTypeIn landscapeIn portrait
SwToPhsw → height
SwToPwsw → width
SwToLhsw → height
SwToLwsw → width
PhToLwheight → width
PwToLhwidth → height
LhToPwheight → width
LwToPhwidth → height
Defaultno swapno swap

Shortcut → inverter mapping:

C# / XAML shortcutInverter
SdpPh / {sdpPh:N}SwToPh
SdpLw / {sdpLw:N}SwToLw
SdpLh / {sdpLh:N}SwToLh
SdpPw / {sdpPw:N}SwToPw
HdpLw / {hdpLw:N}PhToLw
HdpPw / {hdpPw:N}PwToLh
WdpLh / {wdpLh:N}PwToLh
WdpPh / {wdpPh:N}PhToLw

Responsive builders

Equivalent to Android Int.scaledDp() / DimenScaled.

using AppDimens.Maui.Builders;
using AppDimens.Maui.Core;

// Chained shortcuts
double size = Responsive.Value(14)
    .Tablet(18)      // smallestWidth ≥ 600
    .Landscape(16)   // landscape orientation
    .Desktop(20)     // UiModeType.Desk
    .Sdp();

// From int
double fromScaledDp = 14.ScaledDp()
    .Screen(DpQualifier.SmallWidth, 480, 16)
    .Sdp();

// h/w axes
double h = Responsive.Value(14).Tablet(18).Hdp();
double w = Responsive.Value(14).Landscape(16).Wdp();

Builder priorities (Screen)

Rules evaluated in order (Android parity):

PriorityMethodCondition
1Screen(UiModeType, DpQualifier, qualifierValue, customValue, …)UI mode + metric ≥ value
2Screen(UiModeType, customValue, …)UI mode (+ optional orientation)
3Screen(DpQualifier, value, customValue, …)metric ≥ value (+ optional orientation)
4Screen(OrientationQualifier, customValue, …)orientation

Tie-break: lower priority wins; within the same priority, larger qualifierValue wins.

Optional Screen() parameters

.Screen(
    uiMode: UiModeType.Television,
    qualifierType: DpQualifier.SmallWidth,
    qualifierValue: 720,
    customValue: 24,
    finalQualifier: DpQualifier.SmallWidth,  // axis used to resolve customValue
    orientation: OrientationQualifier.Landscape,
    inverter: InverterType.SwToPh)

Advanced example:

var dimen = Responsive.Value(12)
    .Screen(UiModeType.Television, DpQualifier.SmallWidth, 720, 24)
    .Screen(DpQualifier.SmallWidth, 600, 18)
    .Screen(OrientationQualifier.Landscape, 16)
    .Sdp();

UiModeType values

Normal, Television, Car, Watch, Desk, Appliance, VrHeadset, Undefined.


Conditional helpers

Equivalent to Android sdpRotate, sspRotate, sdpQualifier, etc. Available for all axes: sdp/hdp/wdp and ssp/hsp/wsp/sem/hem/wem.

Each helper has short (AppDimens.Maui.Markup), normal (AppDimens.Maui.Xaml), and C# (DimenFacilitators) forms.

using AppDimens.Maui.Core;
using AppDimens.Maui.Helpers;

// Rotate — DP
30.SdpRotate(45);
30.HdpRotate(45);
30.WdpRotate(45);

// Rotate — SP (font)
16.SspRotate(20);
16.HspRotate(20);
16.WspRotate(20);
16.SemRotate(20);
16.HemRotate(20);
16.WemRotate(20);

// Qualifier — DP and SP
16.SdpQualifier(qualifiedValue: 20, qualifierType: DpQualifier.SmallWidth, qualifierThreshold: 600);
18.SspQualifier(22, DpQualifier.SmallWidth, 600);
18.HspQualifier(22, DpQualifier.Height, 800);
18.SemQualifier(20, DpQualifier.SmallWidth, 600);

// Mode — DP and SP
16.SdpMode(modeValue: 24, uiMode: UiModeType.Television);
18.SspMode(26, UiModeType.Television);
18.WemMode(26, UiModeType.Desk);

// Screen — DP and SP
16.SdpScreen(screenValue: 20, uiMode: UiModeType.Television,
    qualifierType: DpQualifier.SmallWidth, qualifierThreshold: 600);
18.SspScreen(22, UiModeType.Television, DpQualifier.SmallWidth, 600);

XAML examples (short · normal):

<!-- Rotate -->
<BoxView WidthRequest="{sdpRotate:30, Rotation=45, Orientation=Landscape}" />
<BoxView WidthRequest="{dimen:SdpRotate Base=30, Rotation=45, Orientation=Landscape}" />

<!-- Qualifier -->
<Label FontSize="{sdpQualifier:16, Qualified=20, QualifierType=SmallWidth, Threshold=600}" />
<Label FontSize="{dimen:SdpQualifier Base=16, Qualified=20, QualifierType=SmallWidth, Threshold=600}" />

<!-- Mode -->
<Label FontSize="{sdpMode:16, Mode=24, UiMode=Desk}" />
<Label FontSize="{dimen:SdpMode Base=16, Mode=24, UiMode=Desk}" />

<!-- Screen -->
<Label FontSize="{sdpScreen:16, Screen=20, UiMode=Television, QualifierType=SmallWidth, Threshold=600}" />
<Label FontSize="{dimen:SdpScreen Base=16, Screen=20, UiMode=Television, QualifierType=SmallWidth, Threshold=600}" />

Optional parameter finalQualifier sets the axis used to resolve the alternate value (default by type: SmallWidth for sdp/ssp/sem, Height for hdp/hsp/hem, Width for wdp/wsp/wem).

Note: helpers like hdpMode, wdpQualifier, etc. for DP still use sdp-prefixed names on Android only; in MAUI use SdpMode/SdpQualifier or AppDimensResolver.Resolve() with an explicit axis.


Converters and physical units

For data binding with {Binding}:

<ContentPage xmlns:conv="clr-namespace:AppDimens.Maui.Converters;assembly=AppDimens.Maui.Converters">
    <ContentPage.Resources>
        <conv:SdpConverter x:Key="SdpConv" />
        <conv:SspConverter x:Key="SspConv" />
        <conv:HspConverter x:Key="HspConv" />
        <conv:WspConverter x:Key="WspConv" />
        <conv:SemConverter x:Key="SemConv" />
        <conv:HdpConverter x:Key="HdpConv" />
        <conv:WdpConverter x:Key="WdpConv" />
        <conv:MmToPxConverter x:Key="MmConv" />
    </ContentPage.Resources>

    <BoxView HeightRequest="{Binding Source=48, Converter={StaticResource HdpConv}}" />
</ContentPage>
ConverterInputOutput
SdpConverterint or double indexdouble sdp
SspConverterint or double indexdouble ssp
HspConverterint or double indexdouble hsp
WspConverterint or double indexdouble wsp
SemConverterint or double indexdouble sem
HdpConverterint or double indexdouble hdp
WdpConverterint or double indexdouble wdp
MmToPxConverterdouble mmpixels (uses current density)

DimenPhysicalUnits (C#)

using AppDimens.Maui.Converters;
using AppDimens.Maui.Core;

double density = AppDimensResolver.Instance.Metrics.Current.Density;

// To pixels
double px = DimenPhysicalUnits.MmToPx(10, density);
double px2 = DimenPhysicalUnits.CmToPx(1, density);
double px3 = DimenPhysicalUnits.InchToPx(1, density);

// Pure conversions (no density)
double inch = DimenPhysicalUnits.MmToInch(25.4);
double cm = DimenPhysicalUnits.MmToCm(100);
double mm = DimenPhysicalUnits.CmToMm(10);
double inchFromCm = DimenPhysicalUnits.CmToInch(2.54);
double mmFromInch = DimenPhysicalUnits.InchToMm(1);

// Geometry
double r = DimenPhysicalUnits.Radius(20, UnitType.Mm, density);
double d = DimenPhysicalUnits.Diameter(10, UnitType.Mm, density);
double circ = DimenPhysicalUnits.Circumference(5, UnitType.Cm, density);
double area = DimenPhysicalUnits.Area(5, UnitType.Cm, density);

UnitType: Inch, Cm, Mm, Sp, Dp, Px.


Static AppDimensSdps API

Alternative without extensions:

using AppDimens.Maui;

AppDimensSdps.Initialize();
AppDimensSdps.Warmup();

double a = AppDimensSdps.Sdp(16);
double b = AppDimensSdps.Ssp(18);
double c = AppDimensSdps.Hsp(18);
double d = AppDimensSdps.Wsp(18);
double e = AppDimensSdps.Hdp(48);
double f = AppDimensSdps.Wdp(120);
double g = AppDimensSdps.Sdpa(16);
double h = AppDimensSdps.Sspa(18);
double i = AppDimensSdps.Sem(18);
double j = AppDimensSdps.Sema(18);

// Advanced access
var resolver = AppDimensSdps.Resolver;
var metrics = resolver.Metrics.Current;

Static methods: Sdp, Ssp, Hsp, Wsp, Hdp, Wdp, Sdpa, Sspa, Hspa, Wspa, Sem, Sema, Hem, Hema, Wem, Wema. Inverters via AppDimensResolver or extensions.


Screen metrics

AppDimensResolver.Instance.Metrics.Current returns ScreenMetricsSnapshot:

FieldDescription
WidthDpLogical width in dp
HeightDpLogical height in dp
SmallestDpmin(WidthDp, HeightDp)
DensityPixel density
`DensityDpi$\text{DPI} (160 \times \text{density})
$Orientation`Portrait or Landscape
UiModeDevice UiModeType

Updated automatically on MainDisplayInfoChanged and on Warmup().


Compatible MAUI properties

Markups return double. Use them on properties that accept double:

PropertyRecommended markup
Padding, Margin{sdp:N} (uniform) or Thickness in C#
FontSize{ssp:N} or {sem:N}
HeightRequest, WidthRequest{sdp:N}, {hdp:N}, {wdp:N}
Spacing, RowSpacing, ColumnSpacing{sdp:N}
CornerRadius{sdp:N}
MinimumHeightRequest, MaximumWidthRequest, etc.any type per axis

Padding and Margin with markups apply a uniform value on all four sides. For per-side values, use C# Thickness.


Migration Android → MAUI

XML / resources

AndroidMAUI (short)MAUI (normal)
@dimen/_16sdp{sdp:16}{dimen:Sdp Value=16}
android:padding="@dimen/_16sdp"Padding="{sdp:16}"Padding="{dimen:Sdp Value=16}"
@dimen/_18sdp (text){ssp:18}{dimen:Ssp Value=18}
@dimen/_48hdp{hdp:48}{dimen:Hdp Value=48}
@dimen/_120wdp{wdp:120}{dimen:Wdp Value=120}
@dimen/_minus8sdp{sdp:-8}{dimen:Sdp Value=-8}
values-sw600dp/automatic via bucket when smallestDp ≥ 600

Kotlin / Java → C#

AndroidMAUI C#
16.sdp16.Sdp() or AppDimensSdps.Sdp(16)
16.ssp16.Ssp()
16.sdpPh16.SdpPh() or {sdpPh:16}
16.sdpa16.Sdpa() or {sdpa:16}
16.sdpRotate(45)16.SdpRotate(45)
16.scaledDp().screen(...)16.ScaledDp().Screen(...).Sdp() or Responsive.Value(16)...
DimenSdp.warmup(ctx)AppDimensSdps.Warmup()
Dimen._16sdp (Kotlin generated)16.Sdp() or AppDimensSdps.Sdp(16)

What does not exist (by design)

  • Android qualifiers -land / -port → use inverters + builders
  • @dimen in project Android res/values → use MAUI markup or C# extensions
  • Compose @Composable API → use MAUI markup or C#
  • {StaticResource _16sdp} XAML ResourceDictionary → use {sdp:16} or 16.Sdp()

Performance and caching

  • DimensionCache: thread-safe ConcurrentDictionary; O(1) lookup after first calculation
  • Automatic invalidation on screen change (rotation, DPI, resize)
  • Warmup(): precomputes aspect-ratio factors (cold start)
  • Hot path: zero reflection; cached DimensionCache lookups
  • Suitable for virtualized lists, animations, and dashboards

Internal BenchmarkDotNet run: cached lookup hit is typically < 50 ns on desktop-class hardware.


Known limitations

ItemStatus
DP helpers hdpMode, wdpQualifier, etc.Helper is sdp-named only; use Resolve() for an explicit axis
PaddingSdp / MarginSdpUniform value only
Buckets in Bucket modeRequires Generated folder accessible at runtime
sema/hema/wemaMAUI extension beyond Android (sem + aspect ratio)
Release trimmingHandled by the package (ILLink.Descriptors.xml + buildTransitive targets); no app-side linker config required

Frequently asked questions

Do I need to call UseAppDimensSdps()?
Yes. Without initialization, metrics stay zero and dimensions will be wrong.

Which mode should I use?
Hybrid (default) for Android parity with a safe fallback. Continuous for math-only sizing. Bucket for discrete values matching Android XML.

Do markups recalculate on rotation?
The resolver yes — metrics and cache update automatically after UseAppDimensSdps(). XAML markup ({sdp:16}, etc.) is evaluated when the page loads; it does not live-bind to rotation unless you update properties in code or re-navigate. C# (16.Sdp() in OnAppearing / SizeChanged) always uses current metrics. See Screen rotation and resize.

Can I use code-behind without XAML?
Yes — 16.Sdp() and AppDimensSdps.Sdp(16) are the primary path.

How do I integrate accessibility font scale?
Call AppDimensResolver.Instance.SetFontScale(...) when system preference changes.

Does it work on Windows/macOS?
Yes — it uses MAUI Essentials DeviceDisplay on all supported platforms.

Release build crashes with MarkupExtension not found for sdp:…?
That was IL trimming removing XAML-only types. Fixed in the package: AppDimens.Maui.Markup, AppDimens.Maui.Xaml, and AppDimens.Maui.Converters ship trimmer roots automatically (NuGet buildTransitive + embedded ILLink.Descriptors.xml). Update to a version that includes this fix; no extra linker XML is required in your app.


Package contents

Bodenberg.AppDimens.Maui.Sdps bundles:

AssemblyRole
AppDimens.MauiAppDimensResolver, UseAppDimensSdps()
AppDimens.Maui.CoreFormulas, enums, cache, metrics
AppDimens.Maui.ResponsiveBuckets, qualifiers, merge
AppDimens.Maui.InvertersAxis inversion engine
AppDimens.Maui.MarkupShort markup {sdp:16}
AppDimens.Maui.XamlNormal markup {dimen:Sdp Value=16}
AppDimens.Maui.Extensions16.Sdp(), PaddingSdp()
AppDimens.Maui.HelpersRotate, qualifier, mode, screen
AppDimens.Maui.BuildersResponsive.Value()
AppDimens.Maui.ConvertersIValueConverter, physical units
AppDimens.Maui.ResourcesBucket data (~358×3 axes)

License

Apache-2.0 — Copyright © Jean Bodenberg

Repository: appdimens-net-binding · Android reference: appdimens-sdps