E2E Test Suite / Smoke Tests (P0) (push) Failing after 9m19s
E2E Test Suite / Critical User Journeys (push) Has been skipped
E2E Test Suite / API Integration Tests (push) Has been skipped
E2E Test Suite / Form Interaction Tests (push) Has been cancelled
E2E Test Suite / Destructive & Chaos Tests (push) Has been cancelled
E2E Test Suite / Cross-Browser Regression (chromium) (push) Has been cancelled
E2E Test Suite / Cross-Browser Regression (firefox) (push) Has been cancelled
E2E Test Suite / Cross-Browser Regression (webkit) (push) Has been cancelled
E2E Test Suite / Mobile Device Tests (push) Has been cancelled
E2E Test Suite / Security Header Tests (push) Has been cancelled
E2E Test Suite / Test Report Summary (push) Has been cancelled
Deploy to Production / Build & Verify (push) Blocked by required conditions
Deploy to Production / Pre-Deploy Tests (push) Blocked by required conditions
Deploy to Production / Deploy to Railway (push) Blocked by required conditions
Deploy to Production / Deploy to Render (push) Blocked by required conditions
Deploy to Production / Deploy to VPS (PM2) (push) Blocked by required conditions
Deploy to Production / Deploy to Fly.io (push) Blocked by required conditions
Deploy to Production / Post-Deploy Verification (push) Blocked by required conditions
Deploy to Production / Notify on Failure (push) Blocked by required conditions
- Rename WhatsApp label from "WhatsApp" (8 chars) to "Chat" (4) so every visible label fits the ≤7-char action-verb policy and never triggers the new text-overflow:ellipsis safety net even at 320px (iPhone SE). - Bump inline SVG icons to 24×24 (was 22), set min-height 64px and min-width 44px per Apple HIG / WCAG 2.5.5 tap-target requirements. - Add white-space:nowrap + text-overflow:ellipsis as defensive safety net, plus a 320-359px breakpoint that nudges label to 11px / padding to 4px-horizontal so glyph-width drift can't cause overflow. - Rewrite aria-labels to be action-led + descriptive even when visible text is terse: "Call us", "Chat with us on WhatsApp", "Get a free quote" (WCAG 2.5.3 name-in-name). - Bump BaseLayout bottom-spacer from 64px → 72px so the new PricingTimelineBand and GuaranteeBand always have an 8px visual buffer above the fixed sticky bar (no overlap). Verified: build green, all 6 tests in tests/sticky-cta.spec.ts pass against the restarted frontend service. Probed live at 320 / 375 / 414 px — every button is 64px tall, ≥103px wide, icons 24×24, labels never truncate. Footer fully clears the bar at scroll-bottom.
403 lines
15 KiB
Plaintext
403 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 WhatsAppFab from '../components/WhatsAppFab.astro';
|
|
import MobileStickyCtaBar from '../components/MobileStickyCtaBar.astro';
|
|
import '../styles/global.css';
|
|
|
|
interface Props {
|
|
title: string;
|
|
description?: string;
|
|
canonicalUrl?: string;
|
|
ogImage?: string;
|
|
noIndex?: boolean;
|
|
}
|
|
|
|
const {
|
|
title,
|
|
description = 'WorkRoot IT Solutions LLP — Expert software development, web & mobile apps, ERP systems, and government IT solutions. Based in Maharashtra, India. Founded 2022.',
|
|
canonicalUrl = Astro.url.href,
|
|
ogImage = '/og-image.jpg',
|
|
noIndex = false,
|
|
} = Astro.props;
|
|
|
|
const siteName = 'WorkRoot IT Solutions LLP';
|
|
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, ERP solutions, government IT systems, cloud services, IT consulting, digital transformation, custom software, enterprise solutions, Angular, React, Next.js, Astro, Spring Boot, Golang, Expo, React Native, MySQL, PostgreSQL" />
|
|
<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" />
|
|
|
|
<!-- Twitter -->
|
|
<meta name="twitter:card" content="summary_large_image" />
|
|
<meta name="twitter:site" content="@GhanwatAjay" />
|
|
<meta name="twitter:creator" content="@GhanwatAjay" />
|
|
<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 LLP",
|
|
"alternateName": ["WorkRoot", "WorkRoot IT Solutions"],
|
|
"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": "2022",
|
|
"founder": {
|
|
"@type": "Person",
|
|
"name": "Ajay Ghanwat"
|
|
},
|
|
"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.app.goo.gl/9bZ5ez1pjbU74ATq5",
|
|
"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": "10:00",
|
|
"closes": "18:00"
|
|
}
|
|
],
|
|
"priceRange": "$$",
|
|
"currenciesAccepted": "INR",
|
|
"paymentAccepted": "Cash, Credit Card, Bank Transfer",
|
|
"sameAs": [
|
|
"https://x.com/GhanwatAjay",
|
|
"https://www.linkedin.com/company/workroot",
|
|
"https://git.workroot.in/WorkRoot/",
|
|
"https://www.facebook.com/profile.php?id=61576503897897"
|
|
],
|
|
"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": "5.0",
|
|
"reviewCount": "20",
|
|
"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 LLP",
|
|
"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 LLP"
|
|
},
|
|
"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 overflow-x-hidden">
|
|
<!-- 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 />
|
|
|
|
<!-- Spacer to ensure footer/content (incl. PricingTimelineBand, GuaranteeBand,
|
|
and any other bottom-of-page sections) clears the mobile sticky CTA bar.
|
|
Bar min-height is 64px; we use 72px here so there is always an 8px
|
|
visual breathing strip between the last band and the sticky bar.
|
|
<md only — bar is hidden at md+. -->
|
|
<div aria-hidden="true" class="md:hidden h-[calc(72px+env(safe-area-inset-bottom,0px))] w-full"></div>
|
|
|
|
<!-- Mobile sticky bottom CTA bar (visible <md) -->
|
|
<MobileStickyCtaBar />
|
|
|
|
<!-- Floating WhatsApp action (global) -->
|
|
<WhatsAppFab />
|
|
|
|
<!-- 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>
|