Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .github/workflows/app-discovery-qa.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Public App Discovery and SEO QA
on:
pull_request:
branches: [main]
paths:
- apps.html
- apps_data.json
- guides/**
- sitemap.xml
- scripts/build_catalog.py
- site_counts.json
- tests/catalog-growth.test.mjs
- .github/workflows/app-discovery-qa.yml
push:
branches: [main]
paths:
- apps.html
- apps_data.json
- guides/**
- sitemap.xml
- scripts/build_catalog.py
- site_counts.json
- tests/catalog-growth.test.mjs
- .github/workflows/app-discovery-qa.yml
permissions:
contents: read
jobs:
discovery-quality:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Confirm catalog generator remains idempotent
run: python3 scripts/build_catalog.py --check
- name: Verify search, referral links and guide listings
run: node --test tests/catalog-growth.test.mjs
93 changes: 93 additions & 0 deletions apps.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@
.store-badge { display:inline-block; margin-top:.4rem; font-size:.78rem; font-weight:600; color:var(--accent); }
footer { text-align:center; color:var(--sub); font-size:.85rem; padding:2rem; }
footer a { color:var(--accent); }
/* Browse purpose or search without hiding cards from non-JS crawlers. */
.finder{margin:0 0 1.5rem;padding:1.1rem;background:var(--card);border:1px solid var(--line);border-radius:14px}
.finder label{display:block;font-weight:700;margin-bottom:.5rem}
.finder input{width:100%;font:inherit;color:var(--ink);background:var(--bg);border:1px solid var(--line);border-radius:8px;padding:.8rem .9rem}
.finder input:focus-visible,.finder button:focus-visible,.finder a:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.finder-filters{display:flex;flex-wrap:wrap;gap:.5rem;margin:1rem 0 .6rem}
.finder-filters button{cursor:pointer;font:inherit;font-size:.85rem;border:1px solid var(--line);border-radius:999px;padding:.4rem .8rem;background:var(--bg);color:var(--ink)}
.finder-filters button[aria-pressed="true"]{background:var(--accent);color:white;border-color:var(--accent)}
.finder-meta{color:var(--sub);font-size:.82rem;margin:.4rem 0}
.finder-guides{margin-top:.6rem;display:flex;flex-wrap:wrap;gap:1rem;font-size:.9rem}
.finder-guides a{color:var(--accent);font-weight:600}
[hidden]{display:none !important}
</style>
<meta property="og:title" content="All Apps – SK APPS">
<meta property="og:description" content="Browse all 92 mobile apps by SK APPS — productivity, education, health, entertainment and more. Free on Google Play.">
Expand All @@ -39,6 +51,13 @@
<meta name="twitter:title" content="All Apps – SK APPS">
<meta name="twitter:description" content="Browse all 92 mobile apps by SK APPS — productivity, education, health, entertainment and more. Free on Google Play.">
<meta name="twitter:image" content="https://appdevelopsk.com/og-image.png">
<script async src="https://www.googletagmanager.com/gtag/js?id=G-5Z588FVC81"></script>
<script>
window.dataLayer = window.dataLayer || [];
window.gtag = function(){ window.dataLayer.push(arguments); };
gtag('js', new Date());
gtag('config', 'G-5Z588FVC81');
</script>
</head>
<body>
<header>
Expand All @@ -47,6 +66,23 @@ <h1>All Apps</h1>
<p>92 free apps on Google Play — productivity, education, health, games and more.</p>
</header>
<main>
<section class="finder" aria-labelledby="finder-title">
<label id="finder-title" for="find-app">Find an app for what you want to do</label>
<input id="find-app" type="search" placeholder="Try: photo quiz, sleep, journal, debate, food" autocomplete="off">
<div class="finder-filters" role="group" aria-label="Browse by purpose">
<button type="button" data-goal="all" aria-pressed="true">All apps</button>
<button type="button" data-goal="quick-games" aria-pressed="false">Quick games</button>
<button type="button" data-goal="habits" aria-pressed="false">Daily habits</button>
<button type="button" data-goal="decisions" aria-pressed="false">Decision helpers</button>
<button type="button" data-goal="ai" aria-pressed="false">AI tools</button>
</div>
<p id="finder-count" class="finder-meta" role="status" aria-live="polite">Showing all apps</p>
<nav class="finder-guides" aria-label="Guides to choosing an app">
<a href="/guides/quick-games/">Find a quick game</a>
<a href="/guides/daily-habits/">Build a daily habit</a>
</nav>
</section>
<p id="finder-empty" class="finder-meta" hidden>No matches. Try another word or choose All apps.</p>
<div class="grid">
<a class="app-card" href="https://play.google.com/store/apps/details?id=jp.appdevelopsk.facesymmetry&amp;utm_source=appdevelopsk&amp;utm_medium=web&amp;utm_campaign=apps_catalog&amp;referrer=utm_source%3Dappdevelopsk%26utm_medium%3Dweb%26utm_campaign%3Dapps_catalog" target="_blank" rel="noopener">
<img src="icons/apps/face-symmetry.png" alt="HangTrue - Level Checker icon" loading="lazy" width="64" height="64">
Expand Down Expand Up @@ -789,5 +825,62 @@ <h3>Chaos - Stranger Missions</h3>
<footer>
&copy; 2026 SK APPS &middot; <a href="/privacy-policy">Privacy</a> &middot; <a href="/terms">Terms</a>
</footer>
<script>
(() => {
const input = document.getElementById('find-app');
const cards = Array.from(document.querySelectorAll('.grid .app-card'));
const buttons = Array.from(document.querySelectorAll('.finder-filters button'));
const count = document.getElementById('finder-count');
const empty = document.getElementById('finder-empty');
const groups = {
'quick-games': ['blur','instant-judge','nodo','brain-burst','snap-judge','daily-duel','percent-pulse','two-truths','key-clash','flinch','tier-rank-me'],
habits: ['true-me','future-me','water-reminder','gratitude-log','gratitude-challenge','mood-log','sleep-prep','eye-break','stretch-reminder','win-log'],
decisions: ['food-roulette','movie-roulette','date-roulette','dinner-home-roulette','trip-roulette','study-roulette','what-to-do','pickwhirl','truth-roulette','quick-local-food'],
ai: ['debate-me','voice-memo','future-me','date-pilot','global-news','pantry-ai','name-maker','idea-market','life-trace']
};
const clean = value => value.normalize('NFKC').toLocaleLowerCase().trim();
let goal = 'all', loggedSearch = false;
const slug = card => {
const src = card.querySelector('img')?.getAttribute('src') || '';
return src.split('/').pop()?.replace(/\.png$/, '') || '';
};
const fire = (event, data) => {
if (typeof window.gtag === 'function') window.gtag('event', event, data);
};
function render() {
const text = clean(input.value);
const terms = text.split(/\s+/).filter(Boolean);
let shown = 0;
cards.forEach(card => {
const app = slug(card), content = clean(card.textContent || '');
const inGoal = goal === 'all' || (groups[goal] || []).includes(app);
const visible = inGoal && terms.every(term => content.includes(term));
card.hidden = !visible;
if (visible) shown++;
});
count.textContent = shown + ' apps shown';
empty.hidden = shown !== 0;
return shown;
}
input.addEventListener('input', () => {
const shown = render();
// Search strings might include personal details. Never transmit them.
if (input.value.trim() && !loggedSearch) {
fire('catalog_search_used', {results: shown, search_length_band: input.value.length < 5 ? 'short' : 'long'});
loggedSearch = true;
}
if (!input.value.trim()) loggedSearch = false;
});
buttons.forEach(button => button.addEventListener('click', () => {
goal = button.dataset.goal;
buttons.forEach(b => b.setAttribute('aria-pressed', String(b === button)));
fire('catalog_filter_used', {goal, results: render()});
}));
cards.forEach(card => card.addEventListener('click', () => {
fire('catalog_store_click', {app: slug(card), goal, source: 'apps_catalog'});
}));
render();
})();
</script>
</body>
</html>
44 changes: 44 additions & 0 deletions guides/daily-habits/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Daily habit apps for journaling, reflection and routines | SK Apps</title>
<meta name="description" content="Find a daily habit app for a photo journal, future-self reflections, water reminders or a gratitude journal.">
<link rel="canonical" href="https://appdevelopsk.com/guides/daily-habits/">
<meta property="og:type" content="article">
<meta property="og:title" content="Daily habit apps for journaling, reflection and routines">
<meta property="og:description" content="Find a daily habit app for a photo journal, future-self reflections, water reminders or a gratitude journal.">
<meta property="og:url" content="https://appdevelopsk.com/guides/daily-habits/">
<meta property="og:image" content="https://appdevelopsk.com/og-image.png">
<meta name="twitter:card" content="summary_large_image">
<script type="application/ld+json">{"@context":"https://schema.org","@type":"ItemList","name":"Daily habit apps for journaling, reflection and routines","itemListElement":[{"@type":"ListItem","position":1,"name":"Daylapse: Daily Timelapse","url":"https://play.google.com/store/apps/details?id=com.trueme.true_me"},{"@type":"ListItem","position":2,"name":"FutureMe - AI Prediction","url":"https://play.google.com/store/apps/details?id=com.futureme.future_me"},{"@type":"ListItem","position":3,"name":"Water Reminder - Hydration","url":"https://play.google.com/store/apps/details?id=jp.appdevelopsk.water_reminder"},{"@type":"ListItem","position":4,"name":"Daily Reflect","url":"https://play.google.com/store/apps/details?id=jp.appdevelopsk.gratitudelog"}]}</script>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-5Z588FVC81"></script>
<script>window.dataLayer=window.dataLayer||[];window.gtag=function(){dataLayer.push(arguments)};gtag('js',new Date());gtag('config','G-5Z588FVC81');</script>
<style>
:root{color-scheme:light dark}*{box-sizing:border-box}body{font-family:system-ui,-apple-system,Segoe UI,sans-serif;line-height:1.65;max-width:890px;padding:24px;margin:auto;background:#101522;color:#e9eef6}a{color:#aab6ff}header{margin-bottom:34px}.crumb{font-size:.9rem}h1{font-size:clamp(2rem,5vw,3rem);line-height:1.12;letter-spacing:-.03em;margin:1rem 0}.lead{font-size:1.13rem;color:#c9d2e0;max-width:64ch}h2{margin-top:2.5rem}.how{background:#1a2233;border:1px solid #394458;border-radius:16px;padding:20px 26px}.how li{padding:8px 0}.app{border:1px solid #394458;background:#1a2233;padding:22px;margin:18px 0;border-radius:16px;display:flex;gap:18px;align-items:flex-start}.app img{border-radius:17px;flex:none;max-width:76px}.app h3{margin:4px 0;font-size:1.2rem}.app p{margin:4px 0 14px;color:#c9d2e0}.app .purpose{color:#99d4ff;font-size:.86rem;font-weight:750}.store{display:inline-block;text-decoration:none;border-radius:9px;padding:10px 15px;background:#6c7bff;color:#fff;font-weight:750}.store:focus-visible,.store:hover{outline:2px solid #fff;outline-offset:2px}footer{margin:3rem 0;padding-top:1rem;border-top:1px solid #394458;color:#c9d2e0}@media(max-width:560px){.app{padding:16px;gap:12px}.app img{width:58px;height:58px}}@media(prefers-color-scheme:light){body{background:#f8f9fc;color:#172034}.lead,.app p,footer{color:#424e60}.app,.how{background:#fff;border-color:#d7deea}.app .purpose{color:#305baf}.store:hover,.store:focus-visible{outline-color:#34415b}}
</style>
</head>
<body>
<header>
<nav class="crumb" aria-label="Breadcrumb"><a href="/">SK Apps</a> / <a href="/apps">All apps</a> / Daily habit apps for journaling, reflection and routines</nav>
<h1>Daily habit apps for journaling, reflection and routines</h1>
<p class="lead">A repeatable routine starts with a small action you can finish. Choose one habit to start with, rather than setting up several trackers at once.</p>
</header>
<main>
<section class="how"><h2>How to choose</h2><ol><li><strong>Prefer to record a moment visually?</strong> Try taking a daily photo and see how a timeline develops.</li>
<li><strong>Prefer writing and reflection?</strong> Use future-self prompts or a gratitude journal.</li>
<li><strong>Need a simple reminder?</strong> Try a hydration reminder instead of a longer journaling ritual.</li></ol></section>
<section aria-labelledby="choices"><h2 id="choices">Apps to explore</h2>
<p>Each option is different. Choose one that matches the kind of break or routine you want, then check its Google Play listing for supported features and availability.</p>
<article class="app"><img src="/icons/apps/true-me.png" alt="Daylapse: Daily Timelapse app icon" width="76" height="76" loading="lazy"><div><p class="purpose">For one photo a day</p><h3>Daylapse: Daily Timelapse</h3><p>Take one photo a day, keep your streak and watch it become a timelapse</p><a class="store" data-app="true-me" href="https://play.google.com/store/apps/details?id=com.trueme.true_me&amp;utm_source=appdevelopsk&amp;utm_medium=guide&amp;utm_campaign=daily_habits&amp;referrer=utm_source%3Dappdevelopsk%26utm_medium%3Dguide%26utm_campaign%3Ddaily_habits" target="_blank" rel="noopener noreferrer">See on Google Play</a></div></article>
<article class="app"><img src="/icons/apps/future-me.png" alt="FutureMe - AI Prediction app icon" width="76" height="76" loading="lazy"><div><p class="purpose">For future-self reflections</p><h3>FutureMe - AI Prediction</h3><p>AI future predictions: write letters to your future self about love &amp; career</p><a class="store" data-app="future-me" href="https://play.google.com/store/apps/details?id=com.futureme.future_me&amp;utm_source=appdevelopsk&amp;utm_medium=guide&amp;utm_campaign=daily_habits&amp;referrer=utm_source%3Dappdevelopsk%26utm_medium%3Dguide%26utm_campaign%3Ddaily_habits" target="_blank" rel="noopener noreferrer">See on Google Play</a></div></article>
<article class="app"><img src="/icons/apps/water-reminder.png" alt="Water Reminder - Hydration app icon" width="76" height="76" loading="lazy"><div><p class="purpose">For a hydration routine</p><h3>Water Reminder - Hydration</h3><p>Water tracker and hydration reminders to drink enough water every day</p><a class="store" data-app="water-reminder" href="https://play.google.com/store/apps/details?id=jp.appdevelopsk.water_reminder&amp;utm_source=appdevelopsk&amp;utm_medium=guide&amp;utm_campaign=daily_habits&amp;referrer=utm_source%3Dappdevelopsk%26utm_medium%3Dguide%26utm_campaign%3Ddaily_habits" target="_blank" rel="noopener noreferrer">See on Google Play</a></div></article>
<article class="app"><img src="/icons/apps/gratitude-log.png" alt="Daily Reflect app icon" width="76" height="76" loading="lazy"><div><p class="purpose">For gratitude and daily reflection</p><h3>Daily Reflect</h3><p>Daily journal for gratitude, wins &amp; mood — build a positive daily habit</p><a class="store" data-app="gratitude-log" href="https://play.google.com/store/apps/details?id=jp.appdevelopsk.gratitudelog&amp;utm_source=appdevelopsk&amp;utm_medium=guide&amp;utm_campaign=daily_habits&amp;referrer=utm_source%3Dappdevelopsk%26utm_medium%3Dguide%26utm_campaign%3Ddaily_habits" target="_blank" rel="noopener noreferrer">See on Google Play</a></div></article>
</section>
<p>Already know what you want? <a href="/apps">Search all published apps</a> or explore <a href="/guides/quick-games/">quick games</a>.</p>
</main>
<footer>These are apps by SK Apps. The links open Google Play; availability and features depend on your region and installed version. No ranking or performance claim is implied.</footer>
<script>document.querySelectorAll('a.store').forEach(a=>a.addEventListener('click',()=>{if(typeof gtag==='function')gtag('event','guide_store_click',{guide:'daily-habits',app:a.dataset.app})}));</script>
</body>
</html>
Loading
Loading