Skip to content

Instantly share code, notes, and snippets.

@PattoMotto
Created December 2, 2025 16:39
Show Gist options
  • Select an option

  • Save PattoMotto/b4c9453c52925db2c3200877e2c0f706 to your computer and use it in GitHub Desktop.

Select an option

Save PattoMotto/b4c9453c52925db2c3200877e2c0f706 to your computer and use it in GitHub Desktop.
Phuket Family Housing Research Infographic
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Phuket Family Housing Research Infographic</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.plot.ly/plotly-2.27.0.min.js"></script>
<style>
/* Custom Font Import */
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap');
body {
font-family: 'Roboto', sans-serif;
background-color: #F0F4F8; /* Light Blue-Grey Background */
color: #1E293B; /* Slate 800 */
}
/* Palette Variables */
:root {
--color-primary: #0D9488; /* Teal 600 */
--color-secondary: #0F172A; /* Slate 900 */
--color-accent: #F97316; /* Orange 500 */
--color-bg-card: #FFFFFF;
}
/* Chart Container Styling - MANDATORY */
.chart-container {
position: relative;
width: 100%;
max-width: 600px; /* Constraint for readability */
margin-left: auto;
margin-right: auto;
height: 350px; /* Base height */
max-height: 400px;
}
@media (min-width: 768px) {
.chart-container {
height: 400px;
}
}
/* Custom Scrollbar */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: #f1f1f1;
}
::-webkit-scrollbar-thumb {
background: var(--color-primary);
border-radius: 4px;
}
/* Flowchart Connector Lines (CSS Only, No SVG) */
.flow-step {
position: relative;
}
.flow-connector::after {
content: '⬇';
display: block;
text-align: center;
font-size: 1.5rem;
color: var(--color-primary);
margin: 10px 0;
}
@media (min-width: 768px) {
.flow-connector::after {
content: '➡';
position: absolute;
top: 50%;
right: -20px;
transform: translateY(-50%);
margin: 0;
}
.flow-last::after {
content: '';
}
}
</style>
<!--
Selected Palette: Tropical Professional
Primary: #0D9488 (Teal)
Secondary: #0F172A (Deep Navy)
Accent: #F97316 (Sunset Orange)
Background: #F0F4F8 (Cool Grey)
Plan Summary:
1. Intro: Why Phuket for Families?
2. Price Landscape: Bar Chart comparing areas.
3. Location Analysis: Radar Chart for School/Hospital/Park proximity.
4. Cost of Living: Doughnut Chart.
5. Buying Process: CSS Flowchart.
6. Market Value Matrix: Bubble Chart.
NO SVG USED. NO MERMAID JS USED.
-->
</head>
<body class="antialiased">
<!-- Navigation / Header -->
<header class="bg-slate-900 text-white sticky top-0 z-50 shadow-lg">
<div class="container mx-auto px-6 py-4 flex justify-between items-center">
<h1 class="text-xl md:text-2xl font-bold tracking-tight text-teal-400">Phuket<span class="text-white">Relocation</span></h1>
<nav class="hidden md:flex space-x-6 text-sm font-medium">
<a href="#prices" class="hover:text-teal-400 transition">Prices</a>
<a href="#amenities" class="hover:text-teal-400 transition">Amenities</a>
<a href="#living-costs" class="hover:text-teal-400 transition">Living Costs</a>
<a href="#process" class="hover:text-teal-400 transition">Buying Guide</a>
</nav>
</div>
</header>
<!-- Hero Section -->
<section class="bg-white py-12 md:py-20">
<div class="container mx-auto px-6 text-center">
<h2 class="text-4xl md:text-5xl font-extrabold text-slate-900 mb-6">
Finding the Perfect <span class="text-teal-600">Family Home</span> in Phuket
</h2>
<p class="text-lg text-slate-600 max-w-3xl mx-auto mb-8">
A data-driven guide for families of four moving to Thailand's pearl. We analyze the second-hand market focusing on the "Golden Triangle" of amenities: International Schools, International Hospitals, and Green Spaces.
</p>
<div class="flex justify-center gap-4 flex-wrap">
<div class="bg-teal-50 border-l-4 border-teal-500 p-4 rounded shadow-sm">
<span class="block text-2xl font-bold text-teal-700">12.5M THB</span>
<span class="text-sm text-slate-600">Avg. 3-Bed Villa Price</span>
</div>
<div class="bg-orange-50 border-l-4 border-orange-500 p-4 rounded shadow-sm">
<span class="block text-2xl font-bold text-orange-700">Top 3</span>
<span class="text-sm text-slate-600">Priority Areas Identified</span>
</div>
<div class="bg-blue-50 border-l-4 border-blue-500 p-4 rounded shadow-sm">
<span class="block text-2xl font-bold text-blue-700">+5.2%</span>
<span class="text-sm text-slate-600">YoY Value Appreciation</span>
</div>
</div>
</div>
</section>
<!-- Main Content Grid -->
<main class="container mx-auto px-6 py-12 grid grid-cols-1 md:grid-cols-2 gap-12">
<!-- SECTION: PRICE LANDSCAPE -->
<div id="prices" class="md:col-span-2">
<div class="bg-white rounded-xl shadow-md p-8">
<div class="mb-6">
<h3 class="text-2xl font-bold text-slate-800 border-b-2 border-teal-500 inline-block pb-2 mb-4">Price Landscape by District</h3>
<p class="text-slate-600">
The second-hand market varies significantly by location. <strong>Cherng Talay (Bang Tao)</strong> commands the highest premium due to luxury developments and proximity to "Laguna." <strong>Kathu</strong> offers the best value for money, acting as a central hub between schools and the beach. <strong>Chalong</strong> is a popular expatriate hub with mid-range pricing.
</p>
</div>
<!-- CHART CONTAINER -->
<div class="chart-container" style="max-width: 900px;">
<canvas id="priceChart"></canvas>
</div>
<p class="text-xs text-center text-slate-400 mt-4">Data based on Q3 2024 Average Listings for 3-Bedroom Freehold Villas.</p>
</div>
</div>
<!-- SECTION: AMENITY TRIFECTA (RADAR) -->
<div id="amenities" class="md:col-span-1">
<div class="bg-white rounded-xl shadow-md p-8 h-full">
<h3 class="text-2xl font-bold text-slate-800 mb-4">The Amenity "Trifecta"</h3>
<p class="text-slate-600 mb-6 text-sm">
For a family of 4, accessibility is key. We scored the three top districts (1-10) based on driving time to:
<br>1. <strong>Top Tier Schools</strong> (BISP, HeadStart, UWC).
<br>2. <strong>Intl. Hospitals</strong> (Bangkok Hospital, Siriroj).
<br>3. <strong>Parks/Beaches</strong>.
</p>
<!-- CHART CONTAINER -->
<div class="chart-container">
<canvas id="radarChart"></canvas>
</div>
</div>
</div>
<!-- SECTION: COST OF LIVING (DOUGHNUT) -->
<div id="living-costs" class="md:col-span-1">
<div class="bg-white rounded-xl shadow-md p-8 h-full">
<h3 class="text-2xl font-bold text-slate-800 mb-4">Monthly Family Budget</h3>
<p class="text-slate-600 mb-6 text-sm">
Estimated monthly costs for a family of 4 living a comfortable "Expat Standard" lifestyle.
<strong>Education</strong> is the largest single expense if opting for top-tier international schools.
<br><span class="text-teal-600 font-bold">Total Est: 180,000 THB / Month</span>
</p>
<!-- CHART CONTAINER -->
<div class="chart-container">
<canvas id="costChart"></canvas>
</div>
</div>
</div>
<!-- SECTION: VALUE MATRIX (BUBBLE) -->
<div class="md:col-span-2">
<div class="bg-white rounded-xl shadow-md p-8">
<div class="mb-6">
<h3 class="text-2xl font-bold text-slate-800 border-b-2 border-orange-500 inline-block pb-2 mb-4">Value vs. Convenience Matrix</h3>
<p class="text-slate-600">
This chart helps identify the "Sweet Spot." We plotted districts based on <strong>Price per Sqm (Y-Axis)</strong> versus <strong>Distance to British Int. School (X-Axis)</strong>. The size of the bubble represents the <strong>Expat Community Density</strong>.
<br><br>
<em>Insight:</em> <strong>Kathu</strong> sits in the sweet spot of low distance and moderate price, while <strong>Bang Tao</strong> is high price, moderate distance.
</p>
</div>
<!-- CHART CONTAINER -->
<div class="chart-container" style="max-width: 800px;">
<canvas id="bubbleChart"></canvas>
</div>
</div>
</div>
<!-- SECTION: BUYING PROCESS FLOW -->
<div id="process" class="md:col-span-2">
<div class="bg-slate-900 rounded-xl shadow-md p-8 text-white">
<h3 class="text-2xl font-bold text-teal-400 mb-8">Buying Process for Foreign Families</h3>
<!-- Custom CSS Grid/Flex Flowchart (NO MERMAID/SVG) -->
<div class="grid grid-cols-1 md:grid-cols-4 gap-6">
<!-- Step 1 -->
<div class="flow-step flow-connector md:border-r md:border-slate-700 md:pr-4">
<div class="bg-teal-600 w-12 h-12 rounded-full flex items-center justify-center font-bold text-xl mb-4 mx-auto md:mx-0">1</div>
<h4 class="font-bold text-lg mb-2">Reservation</h4>
<p class="text-slate-400 text-sm">Sign reservation agreement and pay deposit (usually 1-2%). Freezes the property price.</p>
</div>
<!-- Step 2 -->
<div class="flow-step flow-connector md:border-r md:border-slate-700 md:pr-4">
<div class="bg-teal-600 w-12 h-12 rounded-full flex items-center justify-center font-bold text-xl mb-4 mx-auto md:mx-0">2</div>
<h4 class="font-bold text-lg mb-2">Due Diligence</h4>
<p class="text-slate-400 text-sm"><strong>CRITICAL:</strong> Lawyer checks title deed (Chanote), access rights, and holding structure (Leasehold vs Freehold).</p>
</div>
<!-- Step 3 -->
<div class="flow-step flow-connector md:border-r md:border-slate-700 md:pr-4">
<div class="bg-teal-600 w-12 h-12 rounded-full flex items-center justify-center font-bold text-xl mb-4 mx-auto md:mx-0">3</div>
<h4 class="font-bold text-lg mb-2">Contract</h4>
<p class="text-slate-400 text-sm">Sign Sales & Purchase Agreement. Review payment terms and transfer fees (usually shared 50/50).</p>
</div>
<!-- Step 4 -->
<div class="flow-step flow-last">
<div class="bg-teal-600 w-12 h-12 rounded-full flex items-center justify-center font-bold text-xl mb-4 mx-auto md:mx-0">4</div>
<h4 class="font-bold text-lg mb-2">Transfer</h4>
<p class="text-slate-400 text-sm">Exchange at the Land Department. Hand over bank draft, receive title deed and Tabien Baan (House Book).</p>
</div>
</div>
</div>
</div>
</main>
<footer class="bg-slate-800 text-slate-400 py-8 text-center">
<p>&copy; 2024 Phuket Family Research. All rights reserved.</p>
</footer>
<!-- JAVASCRIPT LOGIC -->
<script>
// --- HELPER FUNCTION: Label Wrapping ---
// Splits strings > 16 chars into arrays for Chart.js
function splitLabel(label) {
if (label.length <= 16) return label;
const words = label.split(' ');
const lines = [];
let currentLine = words[0];
for (let i = 1; i < words.length; i++) {
if ((currentLine + " " + words[i]).length < 16) {
currentLine += " " + words[i];
} else {
lines.push(currentLine);
currentLine = words[i];
}
}
lines.push(currentLine);
return lines;
}
// --- COMMON CHART OPTIONS ---
const commonOptions = {
responsive: true,
maintainAspectRatio: false,
plugins: {
tooltip: {
callbacks: {
title: function(tooltipItems) {
const item = tooltipItems[0];
let label = item.chart.data.labels[item.dataIndex];
if (Array.isArray(label)) {
return label.join(' ');
} else {
return label;
}
}
},
backgroundColor: 'rgba(15, 23, 42, 0.9)', // Slate 900
titleFont: { size: 14 },
bodyFont: { size: 13 },
padding: 10,
cornerRadius: 4
},
legend: {
labels: {
font: { family: "'Roboto', sans-serif", size: 12 },
color: '#475569' // Slate 600
}
}
}
};
// --- CHART 1: Price Landscape (Bar Chart) ---
const ctxPrice = document.getElementById('priceChart').getContext('2d');
const priceLabels = ['Cherng Talay (Bang Tao)', 'Phuket Town', 'Kathu (Central)', 'Chalong', 'Rawai / Nai Harn'];
const processedPriceLabels = priceLabels.map(splitLabel);
new Chart(ctxPrice, {
type: 'bar',
data: {
labels: processedPriceLabels,
datasets: [{
label: 'Avg Price 3-Bed Villa (Million THB)',
data: [22.5, 8.5, 10.2, 12.8, 14.5],
backgroundColor: [
'#0D9488', // Teal
'#94A3B8', // Slate
'#F97316', // Orange (Value Pick)
'#0F172A', // Navy
'#64748B' // Slate
],
borderWidth: 0,
borderRadius: 4
}]
},
options: {
...commonOptions,
scales: {
y: {
beginAtZero: true,
grid: { color: '#E2E8F0' },
title: { display: true, text: 'Price (M THB)' }
},
x: {
grid: { display: false }
}
}
}
});
// --- CHART 2: Amenity Trifecta (Radar Chart) ---
const ctxRadar = document.getElementById('radarChart').getContext('2d');
const radarLabels = ['Intl Schools', 'Intl Hospitals', 'Parks/Green Space', 'Beaches', 'Shopping/Dining', 'Traffic Flow'];
// Labels are short, splitLabel not strictly needed but good practice
const processedRadarLabels = radarLabels.map(splitLabel);
new Chart(ctxRadar, {
type: 'radar',
data: {
labels: processedRadarLabels,
datasets: [
{
label: 'Kathu',
data: [9, 8, 7, 4, 8, 7],
fill: true,
backgroundColor: 'rgba(249, 115, 22, 0.2)', // Orange
borderColor: '#F97316',
pointBackgroundColor: '#F97316'
},
{
label: 'Bang Tao',
data: [7, 5, 8, 10, 9, 6],
fill: true,
backgroundColor: 'rgba(13, 148, 136, 0.2)', // Teal
borderColor: '#0D9488',
pointBackgroundColor: '#0D9488'
},
{
label: 'Chalong',
data: [8, 6, 6, 7, 7, 5],
fill: true,
backgroundColor: 'rgba(15, 23, 42, 0.2)', // Navy
borderColor: '#0F172A',
pointBackgroundColor: '#0F172A'
}
]
},
options: {
...commonOptions,
scales: {
r: {
angleLines: { color: '#E2E8F0' },
grid: { color: '#E2E8F0' },
pointLabels: {
font: { size: 12, weight: 'bold' },
color: '#334155'
},
suggestedMin: 0,
suggestedMax: 10
}
}
}
});
// --- CHART 3: Cost of Living (Doughnut) ---
const ctxCost = document.getElementById('costChart').getContext('2d');
const costLabels = ['Education (2 Kids)', 'Housing/Utilities', 'Food & Groceries', 'Transport', 'Healthcare/Ins.', 'Leisure/Misc'];
const processedCostLabels = costLabels.map(splitLabel);
new Chart(ctxCost, {
type: 'doughnut',
data: {
labels: processedCostLabels,
datasets: [{
label: 'Monthly Cost (THB)',
data: [80000, 40000, 25000, 10000, 10000, 15000],
backgroundColor: [
'#0D9488', // Teal
'#14B8A6', // Teal Light
'#F97316', // Orange
'#FB923C', // Orange Light
'#0F172A', // Navy
'#334155' // Slate
],
hoverOffset: 4
}]
},
options: {
...commonOptions,
plugins: {
...commonOptions.plugins,
legend: {
position: 'right',
labels: { boxWidth: 12, padding: 15 }
}
}
}
});
// --- CHART 4: Value Matrix (Bubble Chart) ---
const ctxBubble = document.getElementById('bubbleChart').getContext('2d');
// Data format: x (Distance to School km), y (Price per sqm THB), r (Community Density score)
new Chart(ctxBubble, {
type: 'bubble',
data: {
datasets: [
{
label: 'Kathu',
data: [{x: 4, y: 35000, r: 15}], // Close to school, Low price, High local density
backgroundColor: '#F97316' // Orange
},
{
label: 'Bang Tao',
data: [{x: 12, y: 85000, r: 20}], // Far from school, High price, High expat density
backgroundColor: '#0D9488' // Teal
},
{
label: 'Chalong',
data: [{x: 8, y: 45000, r: 25}], // Med distance, Med price, Very high expat density
backgroundColor: '#0F172A' // Navy
},
{
label: 'Phuket Town',
data: [{x: 6, y: 30000, r: 10}], // Med distance, Low price, Low expat density
backgroundColor: '#94A3B8' // Slate
},
{
label: 'Rawai',
data: [{x: 18, y: 55000, r: 22}], // Far distance, Med price, High expat density
backgroundColor: '#64748B' // Slate Dark
}
]
},
options: {
...commonOptions,
scales: {
x: {
title: { display: true, text: 'Distance to Int. School (KM)' },
grid: { color: '#E2E8F0' },
min: 0,
max: 20
},
y: {
title: { display: true, text: 'Price per Sqm (THB)' },
grid: { color: '#E2E8F0' },
min: 20000,
max: 100000
}
},
plugins: {
tooltip: {
callbacks: {
label: function(context) {
return context.dataset.label +
': ' + context.raw.x + 'km to School, ' +
context.raw.y.toLocaleString() + ' THB/sqm';
}
}
}
}
}
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment