Design: align app with pocketgraph.app landing page aesthetic

- Dark theme: near-black (#0c0c0c) background, monochromatic palette
- Light theme: clean neutral (#f5f5f5), pure black accent
- Settings page: custom card sections replacing TableView
- Result cards: thin border, reduced corner radius, no shadow
- Query editor: single-border, theme-aware, placeholder text
- Add Connection: monochromatic button hierarchy
- Android: mipmap icons from iOS 1024px source
- Android: overflow icon tinted white via view-tree walk
- Android: status bar appearance set dynamically per theme
- All hardcoded colors replaced with dynamic theme tokens

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Gerwin Kuijntjes
2026-03-31 15:26:47 +02:00
parent e939f04185
commit d0b7e8335d
17 changed files with 254 additions and 86 deletions

5
Xamarin.Neo4j/.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
# Android signing — never commit
*.jks
*.keystore
*.env

View File

@@ -1,8 +1,12 @@
using Android.App;
using Android.Content.PM;
using Android.Content.Res;
using Android.Views;
using AndroidX.AppCompat.Widget;
using AndroidX.Core.View;
using Microsoft.Maui;
using Microsoft.Maui.ApplicationModel;
using AColor = Android.Graphics.Color;
namespace Xamarin.Neo4j.Android
{
@@ -26,11 +30,39 @@ namespace Xamarin.Neo4j.Android
if (IPlatformApplication.Current?.Application is App app)
{
var nightMode = Resources.Configuration.UiMode & UiMode.NightMask;
var theme = nightMode == UiMode.NightYes
? AppTheme.Dark
: AppTheme.Light;
var isDark = nightMode == UiMode.NightYes;
MainThread.BeginInvokeOnMainThread(() => app.UpdateTheme(theme));
// Status bar icon appearance: dark icons on light bg, white icons on dark bg
if (Window != null)
{
var controller = new WindowInsetsControllerCompat(Window, Window.DecorView);
controller.AppearanceLightStatusBars = !isDark;
}
MainThread.BeginInvokeOnMainThread(() =>
app.UpdateTheme(isDark ? AppTheme.Dark : AppTheme.Light));
}
}
public override void OnWindowFocusChanged(bool hasFocus)
{
base.OnWindowFocusChanged(hasFocus);
if (hasFocus)
TintToolbarOverflow(Window?.DecorView as ViewGroup);
}
// MAUI creates its toolbar programmatically so theme-based tinting doesn't reach
// the overflow icon. Walk the view tree and apply the tint directly.
private static void TintToolbarOverflow(ViewGroup? parent)
{
if (parent == null) return;
for (var i = 0; i < parent.ChildCount; i++)
{
var child = parent.GetChildAt(i);
if (child is Toolbar toolbar)
toolbar.OverflowIcon?.SetTint(AColor.White);
else if (child is ViewGroup vg)
TintToolbarOverflow(vg);
}
}
}

View File

@@ -22,5 +22,5 @@
</intent>
</queries>
<application android:label="PocketGraph" android:allowBackup="true"></application>
<application android:label="PocketGraph" android:icon="@mipmap/appicon" android:allowBackup="true"></application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="launcher_background">#FFFFFF</color>
<color name="colorPrimary">#31333b</color>
<color name="colorPrimaryDark">#1e2026</color>
<color name="colorAccent">#31333b</color>
<color name="navBar">#31333b</color>
<color name="launcher_background">#0c0c0c</color>
<color name="colorPrimary">#141414</color>
<color name="colorPrimaryDark">#0c0c0c</color>
<color name="colorAccent">#e2e2e2</color>
<color name="navBar">#141414</color>
</resources>

View File

@@ -12,25 +12,21 @@
<item name="android:windowLightStatusBar">false</item>
</style>
<!-- Main app theme — MAUI draws its own toolbar so we use NoActionBar -->
<style name="MainTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Main app theme — extends MAUI's generated base so all MAUI internals work correctly -->
<style name="MainTheme" parent="Maui.MainTheme.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowActionBar">false</item>
<!-- Light status bar = false so status bar icons show white on dark bg -->
<item name="android:windowLightStatusBar">false</item>
<!-- Context action bar (long-press menu) should match nav bar color -->
<!-- colorOnPrimary controls text/icon color on primary-colored surfaces (toolbar, overflow icon) -->
<item name="colorOnPrimary">#ffffff</item>
<item name="actionModeBackground">@color/colorPrimary</item>
<!-- Make overflow (3-dot) button icon white -->
<item name="actionOverflowButtonStyle">@style/OverflowMenuButton</item>
<!-- Make CAB (contextual action bar) title/icon white without affecting popup menus -->
<item name="actionModeStyle">@style/ActionModeStyle</item>
</style>
<style name="OverflowMenuButton" parent="Widget.AppCompat.ActionButton.Overflow">
<item name="android:tint">@android:color/white</item>
<item name="tint">@android:color/white</item>
</style>
<style name="ActionModeStyle" parent="Widget.AppCompat.ActionMode">

View File

@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<ViewCell xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Xamarin.Neo4j.Controls.LicenseCell">
<StackLayout Padding="15, 15, 15, 0">
<Label FontAttributes="Bold" TextColor="#4169FF" Text="{Binding Name}">
<Label FontAttributes="Bold" TextColor="{DynamicResource Accent}" Text="{Binding Name}">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding OpenRepo}" />
</Label.GestureRecognizers>
</Label>
<ContentView Padding="10" BackgroundColor="#D8D8D8">
<Label Text="{Binding LicenseText}" TextColor="#1a1a1a" />
<ContentView Padding="10" BackgroundColor="{DynamicResource QueryTextBorder}">
<Label Text="{Binding LicenseText}" TextColor="{DynamicResource SecondaryTextColor}" />
</ContentView>
</StackLayout>
</ViewCell>

View File

@@ -14,12 +14,12 @@
<!-- Error display -->
<Border IsVisible="{Binding IsError}"
BackgroundColor="#ffebee"
BackgroundColor="{DynamicResource ErrorBackground}"
StrokeThickness="0"
Padding="12">
<StackLayout Spacing="6">
<Label Text="{Binding ErrorMessage}"
TextColor="#c62828"
TextColor="{DynamicResource ErrorTextColor}"
FontSize="14"
FontFamily="RobotoMono" />
</StackLayout>

View File

@@ -51,9 +51,9 @@
</Grid>
</StackLayout>
<Button Text="Connect" BackgroundColor="#2DB4A8" TextColor="White" Command="{Binding Commands[Connect]}" />
<Button Text="Test" BackgroundColor="Orange" TextColor="White" Command="{Binding Commands[Test]}" />
<Button Text="Save" BackgroundColor="#89972E" TextColor="White" Command="{Binding Commands[Save]}" />
<Button Text="Connect" BackgroundColor="{DynamicResource Accent}" TextColor="{DynamicResource QueryTextBackground}" Command="{Binding Commands[Connect]}" />
<Button Text="Test" BackgroundColor="Transparent" TextColor="{DynamicResource Accent}" BorderColor="{DynamicResource Accent}" BorderWidth="1" Command="{Binding Commands[Test]}" />
<Button Text="Save" BackgroundColor="Transparent" TextColor="{DynamicResource PrimaryTextColor}" BorderColor="{DynamicResource SecondaryTextColor}" BorderWidth="1" Command="{Binding Commands[Save]}" />
</StackLayout>
</ScrollView>
</ContentPage.Content>

View File

@@ -39,10 +39,17 @@
<Grid RowDefinitions="Auto,*">
<!-- Row 0: Query editor -->
<Border Grid.Row="0" BackgroundColor="White" StrokeShape="RoundRectangle 2" Padding="4" Margin="16">
<Border Stroke="#d8e5f1" StrokeThickness="2" StrokeShape="RoundRectangle 2">
<controls:QueryEditor Text="{Binding Query}" ExecuteClicked="ExecuteQuery" FontSize="14" MaxHeight="200" HorizontalOptions="FillAndExpand" AutoSize="TextChanges" />
</Border>
<Border Grid.Row="0"
BackgroundColor="{DynamicResource QueryTextBackground}"
Stroke="{DynamicResource QueryTextBorder}"
StrokeThickness="1"
StrokeShape="RoundRectangle 2"
Padding="4"
Margin="16">
<controls:QueryEditor Text="{Binding Query}" ExecuteClicked="ExecuteQuery"
Placeholder="MATCH (n) RETURN n LIMIT 25"
PlaceholderColor="{DynamicResource SecondaryTextColor}"
FontSize="14" MaxHeight="200" HorizontalOptions="FillAndExpand" AutoSize="TextChanges" />
</Border>
<!-- Row 1: Single scroll area — results then saved queries always visible -->
@@ -136,11 +143,9 @@
<DataTemplate>
<Border Margin="12,0,12,8"
BackgroundColor="{DynamicResource Extreme}"
StrokeShape="RoundRectangle 8"
StrokeThickness="0">
<Border.Shadow>
<Shadow Brush="Black" Offset="0,2" Radius="6" Opacity="0.12" />
</Border.Shadow>
Stroke="{DynamicResource QueryTextBorder}"
StrokeThickness="1"
StrokeShape="RoundRectangle 4">
<StackLayout Spacing="0">
<!-- Action row -->
<Grid ColumnDefinitions="*,*,*,*,Auto" Padding="4,2">

View File

@@ -14,33 +14,156 @@
<ContentPage.Content>
<Grid RowDefinitions="*,Auto">
<TableView Grid.Row="0" Intent="Settings" Background="Transparent" HasUnevenRows="True">
<TableRoot>
<TableSection Title="Support">
<TextCell Text="Contact Support" Command="{Binding Commands[ContactSupport]}" />
<TextCell Text="Rate PocketGraph" Command="{Binding Commands[RateApp]}" />
</TableSection>
<TableSection Title="Data">
<TextCell Text="Clear Saved Connections" Command="{Binding Commands[ClearConnections]}" />
<TextCell Text="Clear Saved Queries" Command="{Binding Commands[ClearQueries]}" />
</TableSection>
<ScrollView Grid.Row="0">
<StackLayout Padding="16" Spacing="24">
<TableSection Title="About">
<TextCell Text="{Binding VersionLabel}" />
<TextCell Text="Software Licenses" Command="{Binding Commands[OpenLicensesPage]}" />
</TableSection>
</TableRoot>
</TableView>
<!-- Support -->
<StackLayout Spacing="8">
<Label Text="SUPPORT"
FontSize="11" FontAttributes="Bold"
CharacterSpacing="2"
TextColor="{DynamicResource SecondaryTextColor}"
Margin="4,0,0,0" />
<Border BackgroundColor="{DynamicResource Extreme}"
Stroke="{DynamicResource QueryTextBorder}"
StrokeThickness="1"
StrokeShape="RoundRectangle 4">
<StackLayout Spacing="0">
<Grid ColumnDefinitions="*,Auto" Padding="16,14">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Commands[ContactSupport]}" />
</Grid.GestureRecognizers>
<Label Grid.Column="0" Text="Contact Support"
TextColor="{DynamicResource PrimaryTextColor}"
VerticalOptions="Center" />
<Label Grid.Column="1"
Text="{x:Static fonts:FontAwesomeSolid.ChevronRight}"
FontFamily="{StaticResource FontAwesomeSolid}"
FontSize="12"
TextColor="{DynamicResource SecondaryTextColor}"
VerticalOptions="Center" />
</Grid>
<BoxView HeightRequest="1" BackgroundColor="{DynamicResource QueryTextBorder}" Margin="16,0" />
<Grid ColumnDefinitions="*,Auto" Padding="16,14">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Commands[RateApp]}" />
</Grid.GestureRecognizers>
<Label Grid.Column="0" Text="Rate PocketGraph"
TextColor="{DynamicResource PrimaryTextColor}"
VerticalOptions="Center" />
<Label Grid.Column="1"
Text="{x:Static fonts:FontAwesomeSolid.ChevronRight}"
FontFamily="{StaticResource FontAwesomeSolid}"
FontSize="12"
TextColor="{DynamicResource SecondaryTextColor}"
VerticalOptions="Center" />
</Grid>
</StackLayout>
</Border>
</StackLayout>
<StackLayout Grid.Row="1" Spacing="15" Padding="25">
<Label Text="A product of" HorizontalOptions="Center" FontAttributes="Italic" FontSize="12" TextColor="{DynamicResource SecondaryTextColor}" />
<Image Source="resoftware.png" HorizontalOptions="Center" WidthRequest="100">
<Image.GestureRecognizers>
<!-- Data -->
<StackLayout Spacing="8">
<Label Text="DATA"
FontSize="11" FontAttributes="Bold"
CharacterSpacing="2"
TextColor="{DynamicResource SecondaryTextColor}"
Margin="4,0,0,0" />
<Border BackgroundColor="{DynamicResource Extreme}"
Stroke="{DynamicResource QueryTextBorder}"
StrokeThickness="1"
StrokeShape="RoundRectangle 4">
<StackLayout Spacing="0">
<Grid ColumnDefinitions="*,Auto" Padding="16,14">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Commands[ClearConnections]}" />
</Grid.GestureRecognizers>
<Label Grid.Column="0" Text="Clear Saved Connections"
TextColor="{DynamicResource PrimaryTextColor}"
VerticalOptions="Center" />
<Label Grid.Column="1"
Text="{x:Static fonts:FontAwesomeSolid.ChevronRight}"
FontFamily="{StaticResource FontAwesomeSolid}"
FontSize="12"
TextColor="{DynamicResource SecondaryTextColor}"
VerticalOptions="Center" />
</Grid>
<BoxView HeightRequest="1" BackgroundColor="{DynamicResource QueryTextBorder}" Margin="16,0" />
<Grid ColumnDefinitions="*,Auto" Padding="16,14">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Commands[ClearQueries]}" />
</Grid.GestureRecognizers>
<Label Grid.Column="0" Text="Clear Saved Queries"
TextColor="{DynamicResource PrimaryTextColor}"
VerticalOptions="Center" />
<Label Grid.Column="1"
Text="{x:Static fonts:FontAwesomeSolid.ChevronRight}"
FontFamily="{StaticResource FontAwesomeSolid}"
FontSize="12"
TextColor="{DynamicResource SecondaryTextColor}"
VerticalOptions="Center" />
</Grid>
</StackLayout>
</Border>
</StackLayout>
<!-- About -->
<StackLayout Spacing="8">
<Label Text="ABOUT"
FontSize="11" FontAttributes="Bold"
CharacterSpacing="2"
TextColor="{DynamicResource SecondaryTextColor}"
Margin="4,0,0,0" />
<Border BackgroundColor="{DynamicResource Extreme}"
Stroke="{DynamicResource QueryTextBorder}"
StrokeThickness="1"
StrokeShape="RoundRectangle 4">
<StackLayout Spacing="0">
<Grid ColumnDefinitions="*,Auto" Padding="16,14">
<Label Grid.Column="0" Text="{Binding VersionLabel}"
TextColor="{DynamicResource PrimaryTextColor}"
VerticalOptions="Center" />
<Label Grid.Column="1"
TextColor="{DynamicResource SecondaryTextColor}"
VerticalOptions="Center" />
</Grid>
<BoxView HeightRequest="1" BackgroundColor="{DynamicResource QueryTextBorder}" Margin="16,0" />
<Grid ColumnDefinitions="*,Auto" Padding="16,14">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Commands[OpenLicensesPage]}" />
</Grid.GestureRecognizers>
<Label Grid.Column="0" Text="Software Licenses"
TextColor="{DynamicResource PrimaryTextColor}"
VerticalOptions="Center" />
<Label Grid.Column="1"
Text="{x:Static fonts:FontAwesomeSolid.ChevronRight}"
FontFamily="{StaticResource FontAwesomeSolid}"
FontSize="12"
TextColor="{DynamicResource SecondaryTextColor}"
VerticalOptions="Center" />
</Grid>
</StackLayout>
</Border>
</StackLayout>
</StackLayout>
</ScrollView>
<StackLayout Grid.Row="1" Spacing="4" Padding="25,16">
<Label HorizontalOptions="Center" FontSize="12">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Commands[OpenReSoftwareSite]}" />
</Image.GestureRecognizers>
</Image>
</Label.GestureRecognizers>
<Label.FormattedText>
<FormattedString>
<Span Text="A product of " TextColor="{DynamicResource SecondaryTextColor}" FontAttributes="Italic" />
<Span Text="Re: Software" TextColor="{DynamicResource Accent}" FontAttributes="Bold" />
</FormattedString>
</Label.FormattedText>
</Label>
</StackLayout>
</Grid>
</ContentPage.Content>
</ContentPage>

View File

@@ -7,13 +7,16 @@
<ResourceDictionary Source="Fonts.xaml" />
</ResourceDictionary.MergedDictionaries>
<Color x:Key="PageBackground">#545863</Color>
<Color x:Key="SecondaryTextColor">#a6a7b2</Color>
<Color x:Key="Accent">#31333b</Color>
<Color x:Key="QueryTextBackground">Black</Color>
<Color x:Key="QueryTextBorder">#686868</Color>
<Color x:Key="QueryActionBar">#686868</Color>
<Color x:Key="Extreme">Black</Color>
<Color x:Key="PageBackground">#0c0c0c</Color>
<Color x:Key="ErrorBackground">#1a0000</Color>
<Color x:Key="ErrorTextColor">#ff6b6b</Color>
<Color x:Key="PrimaryTextColor">#e2e2e2</Color>
<Color x:Key="SecondaryTextColor">#868686</Color>
<Color x:Key="Accent">#e2e2e2</Color>
<Color x:Key="QueryTextBackground">#141414</Color>
<Color x:Key="QueryTextBorder">#2a2a2a</Color>
<Color x:Key="QueryActionBar">#141414</Color>
<Color x:Key="Extreme">#141414</Color>
<Style TargetType="ContentPage" ApplyToDerivedTypes="True">
<Setter Property="BackgroundColor" Value="{StaticResource PageBackground}" />
@@ -21,22 +24,22 @@
<Style TargetType="ListView">
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="SeparatorColor" Value="LightGray" />
<Setter Property="SeparatorColor" Value="#2a2a2a" />
</Style>
<Style TargetType="NavigationPage">
<Setter Property="BarBackgroundColor" Value="{StaticResource Accent}"/>
<Setter Property="BarTextColor" Value="White" />
<Setter Property="BarBackgroundColor" Value="#141414"/>
<Setter Property="BarTextColor" Value="#e2e2e2" />
</Style>
<Style TargetType="TabbedPage" ApplyToDerivedTypes="True">
<Setter Property="BackgroundColor" Value="White"/>
<Setter Property="BarTextColor" Value="White"/>
<Setter Property="SelectedTabColor" Value="White"/>
<Setter Property="BackgroundColor" Value="#141414"/>
<Setter Property="BarTextColor" Value="#868686"/>
<Setter Property="SelectedTabColor" Value="#e2e2e2"/>
</Style>
<Style TargetType="Editor" ApplyToDerivedTypes="True">
<Setter Property="BackgroundColor" Value="White"/>
<Setter Property="TextColor" Value="Black"/>
<Setter Property="BackgroundColor" Value="#141414"/>
<Setter Property="TextColor" Value="#e2e2e2"/>
</Style>
</ResourceDictionary>

View File

@@ -7,13 +7,16 @@
<ResourceDictionary Source="Fonts.xaml" />
</ResourceDictionary.MergedDictionaries>
<Color x:Key="PageBackground">#e4ecf5</Color>
<Color x:Key="PageBackground">#f5f5f5</Color>
<Color x:Key="ErrorBackground">#ffebee</Color>
<Color x:Key="ErrorTextColor">#c62828</Color>
<Color x:Key="PrimaryTextColor">#0c0c0c</Color>
<Color x:Key="SecondaryTextColor">#868a92</Color>
<Color x:Key="Accent">#31333b</Color>
<Color x:Key="QueryTextBackground">#e0e0e0</Color>
<Color x:Key="QueryTextBorder">#31333b</Color>
<Color x:Key="QueryActionBar">#31343B</Color>
<Color x:Key="Extreme">#FFFFFF</Color>
<Color x:Key="Accent">#0c0c0c</Color>
<Color x:Key="QueryTextBackground">#ffffff</Color>
<Color x:Key="QueryTextBorder">#e0e0e0</Color>
<Color x:Key="QueryActionBar">#f5f5f5</Color>
<Color x:Key="Extreme">#ffffff</Color>
<Style TargetType="ContentPage" ApplyToDerivedTypes="True">
<Setter Property="BackgroundColor" Value="{StaticResource PageBackground}" />
@@ -21,25 +24,26 @@
<Style TargetType="ListView">
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="SeparatorColor" Value="#e8e8e8" />
</Style>
<Style TargetType="Entry" ApplyToDerivedTypes="True">
<Setter Property="TextColor" Value="Black" />
<Setter Property="TextColor" Value="#0c0c0c" />
<Setter Property="PlaceholderColor" Value="#868a92" />
</Style>
<Style TargetType="Picker" ApplyToDerivedTypes="True">
<Setter Property="TextColor" Value="Black" />
<Setter Property="TextColor" Value="#0c0c0c" />
</Style>
<Style TargetType="NavigationPage">
<Setter Property="BarBackgroundColor" Value="{StaticResource Accent}"/>
<Setter Property="BarTextColor" Value="White" />
<Setter Property="BarBackgroundColor" Value="#0c0c0c"/>
<Setter Property="BarTextColor" Value="#e2e2e2" />
</Style>
<Style TargetType="TabbedPage" ApplyToDerivedTypes="True">
<Setter Property="BackgroundColor" Value="White"/>
<Setter Property="BarTextColor" Value="Black"/>
<Setter Property="SelectedTabColor" Value="{StaticResource Accent}"/>
<Setter Property="BackgroundColor" Value="#ffffff"/>
<Setter Property="BarTextColor" Value="#868a92"/>
<Setter Property="SelectedTabColor" Value="#0c0c0c"/>
</Style>
</ResourceDictionary>