Files
CompanySite/src/layouts/BaseLayout.astro
T
Clintchiz 0614ae6f85
E2E Test Suite / Critical User Journeys (push) Has been skipped
E2E Test Suite / API Integration Tests (push) Has been skipped
Deploy to Production / Build & Verify (push) Failing after 13s
Ping Search Engines / Notify Search Engines (push) Successful in 3s
Deploy to Production / Pre-Deploy Tests (push) Has been skipped
Deploy to Production / Deploy to Railway (push) Has been skipped
Deploy to Production / Deploy to Render (push) Has been skipped
Deploy to Production / Deploy to VPS (PM2) (push) Has been skipped
Deploy to Production / Deploy to Fly.io (push) Has been skipped
Deploy to Production / Post-Deploy Verification (push) Has been skipped
Deploy to Production / Notify on Failure (push) Successful in 1s
E2E Test Suite / Smoke Tests (P0) (push) Failing after 9m36s
E2E Test Suite / Form Interaction Tests (push) Failing after 12m6s
E2E Test Suite / Destructive & Chaos Tests (push) Failing after 11m46s
E2E Test Suite / Cross-Browser Regression (chromium) (push) Failing after 9m31s
E2E Test Suite / Cross-Browser Regression (firefox) (push) Failing after 11m5s
E2E Test Suite / Cross-Browser Regression (webkit) (push) Failing after 15m24s
E2E Test Suite / Security Header Tests (push) Failing after 7m55s
E2E Test Suite / Test Report Summary (push) Failing after 6s
E2E Test Suite / Mobile Device Tests (push) Failing after 3h12m28s
Uptime Monitor / Health & Response Time (push) Successful in 5s
Uptime Monitor / SSL Certificate (push) Successful in 3s
Uptime Monitor / Send Alerts (push) Has been skipped
Uptime Monitor / Record Uptime Success (push) Successful in 2s
Latest Updated Pages
2026-03-22 14:37:17 +05:30

389 lines
15 KiB
Plaintext

---
// Base Layout for WorkRoot IT Solutions
// Features: SEO meta tags, global styles, header, footer slot, skip link
import Header from '../components/Header.astro';
import Footer from '../components/Footer.astro';
import Analytics from '../components/Analytics.astro';
import PWAInstallPrompt from '../components/PWAInstallPrompt.astro';
import '../styles/global.css';
interface Props {
title: string;
description?: string;
canonicalUrl?: string;
ogImage?: string;
noIndex?: boolean;
}
const {
title,
description = 'WorkRoot IT Solutions - Professional IT services including web development, cloud solutions, and digital transformation for businesses.',
canonicalUrl = Astro.url.href,
ogImage = '/og-image.jpg',
noIndex = false,
} = Astro.props;
const siteName = 'WorkRoot IT Solutions';
const fullTitle = title === 'Home' ? siteName : `${title} | ${siteName}`;
---
<!doctype html>
<html lang="en" class="scroll-smooth">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<!-- Primary Meta Tags -->
<title>{fullTitle}</title>
<meta name="title" content={fullTitle} />
<meta name="description" content={description} />
<meta name="author" content="WorkRoot IT Solutions" />
<meta name="keywords" content="web development, mobile app development, AI solutions, machine learning, cloud services, IT consulting, digital transformation, custom software, enterprise solutions, React, Next.js, React Native, Flutter, AWS, Azure, Python, TensorFlow" />
<meta name="language" content="English" />
<meta name="revisit-after" content="7 days" />
<meta name="rating" content="general" />
<!-- Favicon -->
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<!-- PWA Manifest -->
<link rel="manifest" href="/manifest.json" />
<!-- Canonical URL -->
<link rel="canonical" href={canonicalUrl} />
<!-- DNS Prefetch & Preconnect for external resources -->
<link rel="dns-prefetch" href="https://images.unsplash.com" />
<link rel="preconnect" href="https://images.unsplash.com" crossorigin />
<!-- Robots -->
{noIndex ? (
<meta name="robots" content="noindex, nofollow" />
) : (
<meta name="robots" content="index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1" />
)}
<meta name="googlebot" content="index, follow" />
<meta name="bingbot" content="index, follow" />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content={canonicalUrl} />
<meta property="og:title" content={fullTitle} />
<meta property="og:description" content={description} />
<meta property="og:image" content={new URL(ogImage, Astro.url).href} />
<meta property="og:image:secure_url" content={new URL(ogImage, Astro.url).href} />
<meta property="og:image:type" content="image/jpeg" />
<meta property="og:image:alt" content={fullTitle} />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:site_name" content={siteName} />
<meta property="og:locale" content="en_US" />
<meta property="fb:app_id" content="your-facebook-app-id" />
<!-- Twitter -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@workroot" />
<meta name="twitter:creator" content="@workroot" />
<meta name="twitter:url" content={canonicalUrl} />
<meta name="twitter:title" content={fullTitle} />
<meta name="twitter:description" content={description} />
<meta name="twitter:image" content={new URL(ogImage, Astro.url).href} />
<meta name="twitter:image:alt" content={fullTitle} />
<meta name="twitter:domain" content="workroot.in" />
<meta name="twitter:label1" content="Est. reading time" />
<meta name="twitter:data1" content="3 minutes" />
<!-- Theme Color: dual media query for OS-level dark/light, overridden by JS for stored preference -->
<meta name="theme-color" media="(prefers-color-scheme: light)" content="#0891b2" />
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#0f172a" />
<!-- JSON-LD Structured Data - Organization + LocalBusiness Schema -->
<script type="application/ld+json" set:html={JSON.stringify({
"@context": "https://schema.org",
"@type": ["Organization", "LocalBusiness"],
"name": "WorkRoot IT Solutions",
"alternateName": ["WorkRoot", "WorkRoot IT Solutions LLP"],
"url": "https://workroot.in",
"logo": {
"@type": "ImageObject",
"url": "https://workroot.in/logo.png",
"width": "512",
"height": "512"
},
"image": "https://workroot.in/og-image.jpg",
"description": "Professional IT services including web development, cloud solutions, and digital transformation for businesses.",
"foundingDate": "2014",
"founder": {
"@type": "Person",
"name": "Sarah Chen"
},
"address": {
"@type": "PostalAddress",
"streetAddress": "At Post Rajapur Shantinagar, Taluka Khatav",
"addressLocality": "Satara",
"addressRegion": "MH",
"postalCode": "415503",
"addressCountry": "IN"
},
"geo": {
"@type": "GeoCoordinates",
"latitude": "17.7733632",
"longitude": "74.3309312"
},
"hasMap": "https://maps.google.com/?q=Rajapur+Shantinagar+Khatav+Satara+Maharashtra+415503",
"telephone": "+91-9561417403",
"email": "admin@workroot.in",
"contactPoint": [{
"@type": "ContactPoint",
"telephone": "+91-9561417403",
"contactType": "customer service",
"email": "admin@workroot.in",
"availableLanguage": ["English", "Hindi", "Marathi"],
"areaServed": "Worldwide"
}, {
"@type": "ContactPoint",
"telephone": "+91-9561417403",
"contactType": "sales",
"email": "admin@workroot.in",
"availableLanguage": ["English", "Hindi", "Marathi"],
"areaServed": "Worldwide"
}],
"openingHoursSpecification": [
{
"@type": "OpeningHoursSpecification",
"dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
"opens": "09:00",
"closes": "18:00"
}
],
"priceRange": "$$",
"currenciesAccepted": "INR",
"paymentAccepted": "Cash, Credit Card, Bank Transfer",
"sameAs": [
"https://twitter.com/workroot",
"https://linkedin.com/company/workroot",
"https://github.com/workroot",
"https://facebook.com/workroot"
],
"areaServed": {
"@type": "GeoShape",
"name": "Worldwide"
},
"serviceType": ["Web Development", "Mobile App Development", "AI & Machine Learning", "Cloud Services"],
"knowsAbout": ["Web Development", "Software Engineering", "Cloud Computing", "Artificial Intelligence", "Machine Learning", "Mobile Development"],
"slogan": "Transforming Ideas Into Digital Reality",
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.9",
"reviewCount": "127",
"bestRating": "5",
"worstRating": "1"
}
})} />
<!-- JSON-LD Structured Data - WebSite Schema -->
<script type="application/ld+json" set:html={JSON.stringify({
"@context": "https://schema.org",
"@type": "WebSite",
"name": "WorkRoot IT Solutions",
"url": "https://workroot.in",
"description": "Professional IT services including web development, cloud solutions, and digital transformation for businesses.",
"publisher": {
"@type": "Organization",
"name": "WorkRoot IT Solutions"
},
"potentialAction": {
"@type": "SearchAction",
"target": {
"@type": "EntryPoint",
"urlTemplate": "https://workroot.in/blog?search={search_term_string}"
},
"query-input": "required name=search_term_string"
},
"inLanguage": "en-US"
})} />
<!-- Preconnect to Google Fonts (must come before font stylesheet) -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<!-- Non-blocking font load: Plus Jakarta Sans (variable) + JetBrains Mono (variable) -->
<!-- Replaces the render-blocking @import that was previously in global.css -->
<link
rel="preload"
as="style"
href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:ital,wght@0,200..800;1,200..800&family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&display=swap"
onload="this.onload=null;this.rel='stylesheet'"
/>
<noscript>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:ital,wght@0,200..800;1,200..800&family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&display=swap" />
</noscript>
<!-- Theme init: runs inline before render to prevent FOUC -->
<!-- Also syncs theme-color meta to match stored preference (overrides OS-level media query) -->
<script is:inline>
(function() {
try {
var stored = localStorage.getItem('theme');
var prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
var isDark = stored === 'dark' || (!stored && prefersDark);
if (isDark) document.documentElement.classList.add('dark');
// Sync theme-color meta tags when user has an explicit stored preference
// (without stored preference the OS media query on the meta tags handles it natively)
if (stored) {
var metas = document.querySelectorAll('meta[name="theme-color"]');
var color = isDark ? '#0f172a' : '#0891b2';
metas.forEach(function(m) { m.setAttribute('content', color); });
}
} catch(e) {}
})();
</script>
<!-- Critical CSS for above-the-fold content -->
<style is:inline>
/* Critical CSS for faster FCP - system fonts render immediately */
*,*::before,*::after{box-sizing:border-box}
html{line-height:1.5;-webkit-text-size-adjust:100%;scroll-behavior:smooth}
body{margin:0;font-family:'Plus Jakarta Sans',system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;min-height:100vh;display:flex;flex-direction:column;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}
img,video{max-width:100%;height:auto;display:block}
/* Prevent layout shift from lazy images */
img[loading="lazy"]{background:#e2e8f0}
/* Prevent CLS from images without explicit dimensions */
img{aspect-ratio:attr(width)/attr(height)}
/* Hero section above-the-fold critical styles */
.min-h-screen{min-height:100vh}
.flex{display:flex}
.flex-col{flex-direction:column}
.items-center{align-items:center}
/* Prevent animation flicker on initial load */
.animate-slide-up{animation-fill-mode:forwards}
/* Skip link - always accessible */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}
</style>
<!-- Analytics (GA4 or Plausible) -->
<Analytics />
<!-- Additional head content slot -->
<slot name="head" />
</head>
<body class="min-h-screen flex flex-col antialiased">
<!-- Skip to main content link for accessibility -->
<a
href="#main-content"
class="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 focus:z-[100] focus:px-4 focus:py-2 focus:bg-primary focus:text-white focus:rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-400 focus:ring-offset-2"
>
Skip to main content
</a>
<!-- Header -->
<Header />
<!-- Main Content -->
<main id="main-content" class="flex-1" tabindex="-1">
<slot />
</main>
<!-- Footer -->
<Footer />
<!-- Scripts slot for page-specific scripts -->
<slot name="scripts" />
<!-- Global external link tracking -->
<script>
import { bindExternalLinkTracking } from '../utils/analytics';
document.addEventListener('DOMContentLoaded', bindExternalLinkTracking);
</script>
<!-- Scroll-reveal & micro-interactions -->
<script>
import { initAllAnimations } from '../utils/animations';
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initAllAnimations);
} else {
initAllAnimations();
}
</script>
<!-- Button radial highlight (cursor tracker) -->
<script>
function initButtonHighlight() {
document.querySelectorAll<HTMLElement>('.btn-primary, .btn-primary-lg, .btn-accent').forEach((btn) => {
btn.addEventListener('mousemove', (e: MouseEvent) => {
const rect = btn.getBoundingClientRect();
const x = ((e.clientX - rect.left) / rect.width) * 100;
const y = ((e.clientY - rect.top) / rect.height) * 100;
btn.style.setProperty('--x', `${x}%`);
btn.style.setProperty('--y', `${y}%`);
});
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initButtonHighlight);
} else {
initButtonHighlight();
}
</script>
<!-- Service Worker Registration -->
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker
.register('/sw.js', { scope: '/' })
.catch(() => {
// SW registration failed silently — site still works normally
});
});
}
</script>
<!-- PWA Install Prompt -->
<PWAInstallPrompt />
</body>
</html>
<style is:global>
/* Smooth scrolling for anchor links */
html {
scroll-behavior: smooth;
}
/* Focus styles are defined in global.css via Tailwind ring utilities.
Removed duplicate outline here to avoid conflicting with ring-based focus styles. */
/* Remove focus outline for mouse users */
:focus:not(:focus-visible) {
outline: none;
}
/* Selection color */
::selection {
background-color: theme('colors.primary.200');
color: theme('colors.secondary.900');
}
/* Custom scrollbar (optional - webkit only) */
::-webkit-scrollbar {
width: 10px;
}
::-webkit-scrollbar-track {
background: theme('colors.secondary.100');
}
::-webkit-scrollbar-thumb {
background: theme('colors.secondary.300');
border-radius: 5px;
}
::-webkit-scrollbar-thumb:hover {
background: theme('colors.secondary.400');
}
</style>