docs: homepage redesign, mermaid diagrams, cookbook screenshots, and Laminar observability (#5046)

This commit is contained in:
Naman 2026-03-13 00:02:45 +05:30 committed by GitHub
parent 3b4f72e513
commit ce4c3e2b70
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 3108 additions and 1192 deletions

View file

@ -200,10 +200,12 @@ This uses 5 blocks across 3 categories: browser automation, data and extraction,
### What you built
```
[find_hiring_thread] → [scrape_listings] → [process_each (loop)] → [send_digest]
Browser Task Extraction └─ [summarize] Send Email
Text Prompt
```mermaid
flowchart LR
A["find_hiring_thread<br/>Browser Task"] --> B["scrape_listings<br/>Extraction"]
B --> C["process_each<br/>Loop"]
C --> D["summarize<br/>Text Prompt"]
D --> E["send_digest<br/>Send Email"]
```
Five blocks, three categories, zero code. You can now re-run this workflow anytime, tweak the extraction schema to pull different fields, swap the email for an HTTP Request to post to Slack, or add a Conditional block to only email when listings match certain criteria.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -27,6 +27,13 @@ Each cookbook walks through the workflow step-by-step, explaining design decisio
>
Search for jobs on any portal, extract listings, generate tailored answers with AI, and submit applications automatically.
</Card>
<Card
title="Healthcare Portal Data Extraction"
icon="hospital-user"
href="/cookbooks/healthcare-portal-data"
>
Extract patient demographics and billing data from EHR portals like OpenEMR using residential proxies and browser profiles.
</Card>
</CardGroup>
---
@ -46,7 +53,7 @@ All cookbooks require a Skyvern API key. Here's how to get one:
</Frame>
</Step>
<Step title="Set your environment variable">
Set the `SKYVERN_API_KEY` environment variable in your shell or `.env` file.
Set the `SKYVERN_API_KEY` environment variable in your shell.
```bash
export SKYVERN_API_KEY="your-api-key"
```

View file

@ -0,0 +1,210 @@
---
title: Observability with Laminar
subtitle: Add observability to your Skyvern automations with Laminar
slug: debugging/observability-with-laminar
---
[Laminar](https://www.lmnr.ai/) is an observability platform for AI applications. When integrated with Skyvern, it captures traces of your automation runs in Laminar's dashboard. What you see depends on how you're running Skyvern:
- **Skyvern Cloud (via SDK)** — Laminar wraps the `run_task` / `run_workflow` call, so you get a trace span around the API request and response: latency, status, errors, and the returned output.
- **Self-hosted** — Skyvern's server can export full traces to Laminar, including every LLM call (prompts, responses, token usage), browser actions, and workflow step execution. See [self-hosted tracing setup](#self-hosted-tracing-setup) below.
<Tip>
Laminar traces complement [artifacts](/debugging/using-artifacts). Use artifacts for per-run debugging (screenshots, recordings, logs) and Laminar for tracking patterns across runs — failure rates, response times, and which tasks are slowest.
</Tip>
<Note>
This guide is also available in the [Laminar documentation](https://docs.lmnr.ai/tracing/integrations/skyvern).
</Note>
---
## Prerequisites
Laminar integration requires a Skyvern SDK and the Laminar SDK:
<CodeGroup>
```bash Python
pip install skyvern 'lmnr[all]'
```
```bash TypeScript
npm install @skyvern/client @lmnr-ai/lmnr
```
</CodeGroup>
You will also need:
- A **Skyvern API key** — get one at [app.skyvern.com/settings](https://app.skyvern.com/settings/api-keys)
- A **Laminar API key** — sign up at [lmnr.ai](https://www.lmnr.ai/) and create a project
---
## Set up environment variables
Add both keys to your `.env` file:
```bash .env
SKYVERN_API_KEY=your-skyvern-api-key
LMNR_PROJECT_API_KEY=your-laminar-api-key
```
<Frame>
<video
autoPlay
controls
muted
playsInline
className="w-full aspect-video rounded-xl"
src="/images/laminar-keys.mp4"
></video>
</Frame>
---
## Run a traced task
This example scrapes the top 3 posts from Hacker News with Laminar tracing enabled. Call `Laminar.initialize()` before any Skyvern calls — it reads `LMNR_PROJECT_API_KEY` from your environment automatically.
<CodeGroup>
```python Python
import os
import asyncio
from dotenv import load_dotenv
load_dotenv()
from lmnr import Laminar
Laminar.initialize()
from skyvern import Skyvern
client = Skyvern(api_key=os.getenv("SKYVERN_API_KEY"))
async def main():
result = await client.run_task(
prompt="Get the title and URL of the top 3 posts on Hacker News.",
url="https://news.ycombinator.com",
wait_for_completion=True,
)
print(f"Status: {result.status}")
print(f"Output: {result.output}")
if __name__ == "__main__":
asyncio.run(main())
```
```typescript TypeScript
import { Laminar } from "@lmnr-ai/lmnr";
Laminar.initialize();
import { Skyvern } from "@skyvern/client";
const client = new Skyvern({
apiKey: process.env.SKYVERN_API_KEY,
});
async function main() {
const result = await client.runTask({
body: {
prompt: "Get the title and URL of the top 3 posts on Hacker News.",
url: "https://news.ycombinator.com",
},
waitForCompletion: true,
});
console.log(`Status: ${result.status}`);
console.log(`Output: ${JSON.stringify(result.output, null, 2)}`);
}
main();
```
</CodeGroup>
Expected output:
```json
{
"status": "completed",
"output": {
"posts": [
{"title": "Zig Type Resolution Redesign and Language Changes", "url": "https://ziglang.org/devlog/2026/..."},
{"title": "Create value for others and don't worry about the returns", "url": "https://geohot.github.io/..."},
{"title": "U+237C ⍼ Is Azimuth", "url": "https://ionathan.ch/2026/02/16/angzarr.html"}
]
}
}
```
The task runs, the output is returned, and the full trace — every HTTP call, timing, and payload — appears in your Laminar dashboard.
<Note>
**Python only:** You will see a `ForgeApp is not initialized` error in stderr on startup. This is harmless — `lmnr[all]` tries to instrument Skyvern's server-side internals, which aren't present when using the client SDK. Your traces still work correctly.
</Note>
<Warning>
**Python only:** `LaminarLiteLLMCallback` is deprecated and unnecessary. Laminar instruments LiteLLM directly — no callback setup is needed.
</Warning>
---
## What traces capture
What shows up in Laminar depends on your setup.
### Skyvern Cloud (via SDK)
When calling `run_task` or `run_workflow` through the SDK, Laminar traces the client-side call:
| Trace data | What it shows |
|------------|---------------|
| API request/response | The full round-trip to Skyvern's API — status, latency, payload size |
| Task output | The extracted data or completion result |
| Errors | HTTP errors, timeouts, and task failures |
This is useful for monitoring how your application interacts with Skyvern — tracking which tasks fail, how long they take, and what outputs you're getting back.
### Self-hosted
When running Skyvern on your own infrastructure, you get deep server-side traces by configuring Laminar in Skyvern's environment. This gives you visibility into everything happening inside the agent:
| Trace data | What it shows |
|------------|---------------|
| LLM interactions | Every prompt sent to the model and its response, including token counts |
| Browser actions | Each click, type, and navigation the agent performed |
| Workflow steps | Sequential block execution and data passed between blocks |
| Image tracing | Screenshots sent to the LLM for analysis |
| Performance metrics | Latency and cost per LLM call |
| Errors | Exceptions at any layer — LLM, browser, workflow engine |
---
## Self-hosted tracing setup
If you're running Skyvern on your own infrastructure, add these to your server's environment:
```bash .env
LMNR_PROJECT_API_KEY=your-laminar-api-key
```
Skyvern's server includes a built-in `LaminarTrace` integration that initializes Laminar with the LiteLLM callback, capturing every LLM call, token count, and cost. It disables the automatic Skyvern/Patchright instrumentors (to avoid conflicts) and uses Laminar's `@observe` decorator on internal methods instead.
No code changes needed — once the env var is set, traces appear in your Laminar project automatically.
---
## Next steps
<CardGroup cols={2}>
<Card
title="Using Artifacts"
icon="file-lines"
href="/debugging/using-artifacts"
>
Per-run recordings, screenshots, logs, and network data
</Card>
<Card
title="Troubleshooting Guide"
icon="wrench"
href="/debugging/troubleshooting-guide"
>
Common issues and how to fix them
</Card>
</CardGroup>

View file

@ -1,26 +1,53 @@
{
"$schema": "https://mintlify.com/docs.json",
"theme": "mint",
"theme": "palm",
"name": "Skyvern",
"colors": {
"primary": "#6366F1",
"light": "#818CF8",
"dark": "#4F46E5"
"primary": "#4A6CF7",
"light": "#7B93FA",
"dark": "#1B2559"
},
"appearance": {
"default": "light"
},
"background": {
"color": {
"light": "#F8F5EF",
"dark": "#1C1917"
}
},
"fonts": {
"heading": {
"family": "Inter",
"weight": 600
},
"body": {
"family": "Inter",
"weight": 400
}
},
"logo": {
"light": "/logo/light.svg",
"dark": "/logo/dark.svg",
"light": "/logo/light.png",
"dark": "/logo/dark.png",
"href": "https://skyvern.com"
},
"favicon": "/favicon.svg",
"favicon": "/favicon.png",
"search": {
"prompt": "Search Skyvern docs..."
},
"navigation": {
"tabs": [
{
"tab": "Home",
"pages": ["index"]
},
{
"tab": "Documentation",
"groups": [
{
"group": "Getting Started",
"pages": [
"getting-started/introduction",
"getting-started/quickstart",
"getting-started/core-concepts"
]
@ -46,7 +73,6 @@
"group": "Optimization",
"pages": [
"optimization/browser-sessions",
"optimization/browser-tunneling",
"optimization/browser-profiles",
"optimization/cost-control"
]
@ -68,6 +94,7 @@
"pages": [
"debugging/using-artifacts",
"debugging/troubleshooting-guide",
"debugging/observability-with-laminar",
"debugging/faq"
]
},
@ -191,9 +218,7 @@
},
{
"tab": "Changelog",
"pages": [
"changelog"
]
"pages": ["changelog"]
},
{
"tab": "API Reference",
@ -221,6 +246,7 @@
"twitter": "https://twitter.com/skyvernai"
}
},
"css": "/style.css",
"api": {
"baseUrl": "https://api.skyvern.com",
"auth": {
@ -230,5 +256,139 @@
"playground": {
"display": "interactive"
}
}
},
"redirects": [
{
"source": "/introduction",
"destination": "/getting-started/quickstart"
},
{
"source": "/getting-started/skyvern-in-action",
"destination": "/getting-started/core-concepts"
},
{
"source": "/getting-started/prompting-guide",
"destination": "/debugging/troubleshooting-guide"
},
{
"source": "/running-tasks/run-tasks",
"destination": "/running-automations/run-a-task"
},
{
"source": "/running-tasks/visualizing-results",
"destination": "/debugging/using-artifacts"
},
{
"source": "/running-tasks/cancel-runs",
"destination": "/running-automations/run-a-task"
},
{
"source": "/running-tasks/webhooks-faq",
"destination": "/going-to-production/webhooks"
},
{
"source": "/running-tasks/connect-local-browser",
"destination": "/self-hosted/browser"
},
{
"source": "/running-tasks/proxy-location",
"destination": "/going-to-production/proxy-geolocation"
},
{
"source": "/running-tasks/advanced-features",
"destination": "/running-automations/task-parameters"
},
{
"source": "/running-tasks/api-spec",
"destination": "/running-automations/run-a-task"
},
{
"source": "/workflows/manage-workflows",
"destination": "/cloud/building-workflows/manage-workflows"
},
{
"source": "/workflows/workflow-blocks-details",
"destination": "/multi-step-automations/workflow-blocks-reference"
},
{
"source": "/workflows/workflow-parameters",
"destination": "/multi-step-automations/workflow-parameters"
},
{
"source": "/workflows/run-workflows",
"destination": "/cloud/building-workflows/run-a-workflow"
},
{
"source": "/workflows/consistent-workflows",
"destination": "/going-to-production/reliability-tips"
},
{
"source": "/workflows/running-workflows",
"destination": "/cloud/building-workflows/run-a-workflow"
},
{
"source": "/workflows/introduction",
"destination": "/multi-step-automations/build-a-workflow"
},
{
"source": "/workflows/workflow-blocks",
"destination": "/multi-step-automations/workflow-blocks-reference"
},
{
"source": "/workflows/creating-workflows",
"destination": "/multi-step-automations/build-a-workflow"
},
{
"source": "/workflows/getting-workflows",
"destination": "/multi-step-automations/build-a-workflow#step-7:-get-results"
},
{
"source": "/workflows/what-is-a-parameter",
"destination": "/multi-step-automations/workflow-parameters"
},
{
"source": "/credentials/introduction",
"destination": "/cloud/managing-credentials/credentials-overview"
},
{
"source": "/credentials/passwords",
"destination": "/cloud/managing-credentials/password-credentials"
},
{
"source": "/credentials/credit-cards",
"destination": "/cloud/managing-credentials/credit-card-credentials"
},
{
"source": "/credentials/totp",
"destination": "/cloud/managing-credentials/totp-setup"
},
{
"source": "/credentials/bitwarden",
"destination": "/cloud/managing-credentials/external-providers"
},
{
"source": "/credentials/custom-credential-service",
"destination": "/cloud/managing-credentials/external-providers"
},
{
"source": "/browser-sessions/introduction",
"destination": "/optimization/browser-sessions"
},
{
"source": "/browser-sessions/browser-profiles",
"destination": "/optimization/browser-profiles"
},
{
"source": "/observability/overview",
"destination": "/debugging/using-artifacts"
},
{
"source": "/integrations/make.com",
"destination": "/integrations/make"
},
{
"source": "/integrations/ollama-litellm",
"destination": "/integrations/local-llms"
}
]
}

BIN
docs/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View file

@ -56,18 +56,23 @@ When you need reusable, multi-step automations, use **Workflows** instead.
A **Workflow** is a reusable template composed of blocks. Unlike tasks, workflows can be versioned, shared across your team, and executed repeatedly with different parameters.
```mermaid
flowchart LR
A["Navigate<br/>Block"] --> B["Login<br/>Block"]
B --> C["Extract<br/>Block"]
C --> D["Download<br/>Block"]
subgraph WORKFLOW
direction LR
A
B
C
D
end
```
┌─────────────────────────────────────────────────────────────┐
│ WORKFLOW │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Navigate │ → │ Login │ → │ Extract │ → │ Download │ │
│ │ Block │ │ Block │ │ Block │ │ Block │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ Parameters: {{search_query}}, {{max_price}} │
│ Each block can reference outputs from previous blocks │
└─────────────────────────────────────────────────────────────┘
```
> **Parameters:** `{{search_query}}`, `{{max_price}}`
> Each block can reference outputs from previous blocks.
```python
result = await skyvern.run_workflow(
@ -141,16 +146,14 @@ For detailed block configuration, see [Workflow Blocks](/workflows/workflow-bloc
Every time you execute a task or workflow, Skyvern creates a **Run** to track progress and store outputs.
```
┌→ completed
├→ failed
created → queued → running ─┼→ timed_out
├→ terminated
└→ canceled
```mermaid
flowchart LR
created --> queued --> running
running --> completed
running --> failed
running --> timed_out
running --> terminated
running --> canceled
```
```python
@ -191,29 +194,12 @@ print(result.output) # {"heading": "Welcome"}
Credentials provide secure storage for authentication data. Skyvern encrypts credentials at rest and in transit, and injects them directly into the browser—**credentials are never sent to or seen by the LLM**.
```
┌─────────────────────────────────────────────────────────────┐
│ SECURITY MODEL │
│ │
│ You store credentials Skyvern encrypts & stores │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────────┐ │
│ │ Skyvern UI │ │ Encrypted Vault │ │
│ │ or API │ ─────────── │ (or Bitwarden, │ │
│ └─────────────┘ │ 1Password) │ │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Login Block │ │
│ │ injects into │ │
│ │ browser fields │ │
│ └─────────────────┘ │
│ │ │
│ LLM sees: "login happened" │
│ LLM never sees: actual credentials │
└─────────────────────────────────────────────────────────────┘
```mermaid
flowchart TD
A["You store credentials"] --> B["Skyvern UI<br/>or API"]
B --> C["Encrypted Vault<br/>(or Bitwarden, 1Password)"]
C --> D["Login Block<br/>injects into<br/>browser fields"]
D --> E["LLM sees: 'login happened'<br/>LLM never sees: actual credentials"]
```
**Supported credential types:**
@ -235,24 +221,23 @@ See [Credentials](/sdk-reference/credentials) for setup instructions.
Skyvern offers two ways to manage browser state across runs:
```mermaid
flowchart LR
subgraph SESSION ["Browser Session (live, temporary)"]
A["Active Browser<br/>Instance"]
end
subgraph PROFILE ["Browser Profile (saved, reusable)"]
B["Snapshot of state<br/>(cookies, storage)"]
end
A -- "save" --> B
B -- "restore" --> A
```
┌─────────────────────────────────────────────────────────────┐
│ │
│ BROWSER SESSION BROWSER PROFILE │
│ (live, temporary) (saved, reusable) │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Active │ ──save──▶ │ Snapshot │ │
│ │ Browser │ │ of state │ │
│ │ Instance │ ◀──restore── │ (cookies, │ │
│ └─────────────┘ │ storage) │ │
│ └─────────────┘ │
│ • Expires after timeout • Persists indefinitely │
│ • For real-time chaining • For skipping login │
│ • Max 24 hours • Shared across team │
│ │
└─────────────────────────────────────────────────────────────┘
```
| | Browser Session | Browser Profile |
|---|---|---|
| **Lifetime** | Expires after timeout (max 24h) | Persists indefinitely |
| **Use case** | Real-time task chaining | Skip login on repeated runs |
| **Sharing** | Single use | Shared across team |
### Browser Sessions

View file

@ -0,0 +1,76 @@
---
title: What is Skyvern?
subtitle: AI-powered browser automation for any website
slug: getting-started/introduction
---
Skyvern automates anything you can do in a browser. You describe what you want in plain English, and Skyvern opens a real Chromium browser, looks at the page, and completes the task. No CSS selectors, no scripts, no maintenance when the site changes.
The reason this works is that Skyvern doesn't rely on hardcoded element paths. It screenshots the page, reads the DOM, and uses an LLM to decide what to do next, the same way a person would look at a screen and figure out where to click. That means it works on sites it's never seen before, and it keeps working when those sites redesign.
---
## The agent loop
Every Skyvern automation, whether you trigger it from the dashboard, the API, or a Zapier workflow, runs the same loop:
<img src="/images/skyvern-agent-loop.png" alt="Skyvern agent loop: Screenshot → Extract DOM → LLM reasons → Execute action → Goal check → Repeat" />
Each cycle through this loop is called a **step**. Understanding what happens in each step helps you write better prompts and debug runs when something goes wrong.
1. **Screenshot the viewport.** Skyvern captures what's currently on screen, giving the LLM visual context: where buttons are, what forms look like, whether a modal is blocking the page.
2. **Extract the DOM.** The visible page is scraped into a simplified tree of interactive elements (inputs, buttons, links, dropdowns) with their labels and positions. The LLM uses both the screenshot and the DOM together: the image shows layout and visual context, the DOM provides precise element identifiers that Playwright can target.
3. **LLM decides the next action.** The screenshot, DOM tree, and your original prompt go to the LLM. It picks which element to interact with and what to do: click, type, select, scroll, upload. If there's data to extract, it pulls that too.
4. **Playwright executes.** The action runs in a real Chromium browser. If credentials are needed, they're injected directly into the browser at this point. The LLM never sees passwords, TOTP codes, or credit card numbers.
5. **Check if the goal is met.** If not, loop back to step 1 and screenshot the now-changed page. If yes, return the results.
A typical task takes 2 to 10 steps. You can set `max_steps` to cap cost during development.
### The Planner-Agent-Validator system
For anything beyond simple single-page tasks, Skyvern 2.0 wraps the agent loop in a higher-level system with three components:
<img src="/images/skyvern-system-architecture.png" alt="Skyvern 2.0 architecture: Planner breaks prompt into sub-tasks, Task Agent executes them, Validator confirms completion" />
The **Planner** takes your prompt and breaks it into an ordered sequence of sub-tasks. If you say "log into the vendor portal, download all invoices from January, and save them as PDFs," the Planner sequences that into: navigate to login → authenticate → find the invoice section → loop through January invoices → download each one.
The **Task Agent** picks up each sub-task and runs the agent loop described above. It's focused on one thing at a time, which makes it more reliable than trying to handle a complex multi-page flow in a single pass.
The **Validator** checks whether the overall goal was actually met after the Task Agent finishes. If the Validator determines something was missed or went wrong, it sends feedback back to the Planner, which can re-sequence or retry. This closed-loop feedback is what makes Skyvern reliable on complex workflows where a single missed step would break the whole thing.
---
## What Skyvern handles for you
Most browser automation breaks down at authentication, CAPTCHAs, or dynamic content. Skyvern handles these natively so you don't have to build workarounds.
**Authentication and credentials.** You store passwords, TOTP secrets, and credit card numbers through the Credentials API or the dashboard. They're encrypted at rest and injected directly into browser fields during execution. The LLM orchestrates the login flow (finds the form, clicks submit, handles 2FA prompts) but never sees the actual credential values. Supports Bitwarden, 1Password, and Azure Key Vault as external sources.
**CAPTCHAs.** Automatically detected and solved during execution. You don't configure anything.
**Structured data extraction.** You can pass a JSON Schema defining exactly what fields you want, and Skyvern extracts typed data that conforms to it. Or you can skip the schema and let Skyvern infer structure from your prompt.
**File operations.** Download files from websites, upload documents to form fields, parse PDFs, CSVs, and Excel files. Downloaded files come back as signed URLs with checksums.
**Proxy and geolocation.** Residential proxies route through real IPs in 30+ countries. Set `proxy_location` per task when you need to appear from a specific region.
**Browser state persistence.** Sessions keep a live browser open across multiple tasks for up to 24 hours, useful when you need to chain tasks that share login state. Profiles snapshot cookies, auth tokens, and local storage into a reusable package you can restore on future runs, so you don't re-authenticate every time.
**Multi-step workflows.** When a single task isn't enough, Workflows let you chain blocks together: navigate, login, loop through a list, extract data, branch on conditions, send emails. Workflows are parameterized and version-controlled. You can build them visually in the dashboard or define them through the API.
---
## Get started
<CardGroup cols={2}>
<Card title="Automate from the dashboard" icon="window-maximize" href="/cloud/getting-started/overview">
Type what you want, watch it happen live, get results back. Build multi-step workflows with the visual editor. Connect to Zapier, Make, or n8n. No code required.
</Card>
<Card title="Integrate via API or SDK" icon="code" href="/getting-started/quickstart">
Install the Python or TypeScript SDK, get an API key, and run your first automation in 5 minutes.
</Card>
</CardGroup>
<Card title="Deploy on your own infrastructure" icon="server" href="/self-hosted/overview">
Skyvern is open-source. Run it with Docker, connect your own LLM keys, and keep all data on your network.
</Card>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 966 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

151
docs/index.mdx Normal file
View file

@ -0,0 +1,151 @@
---
title: Skyvern Documentation
slug: /
mode: custom
---
<div className="sk-page">
{/* ===== HERO ===== */}
<div className="sk-hero">
<div className="sk-hero-inner">
<div className="sk-hero-left">
<div style={{ display: "flex", alignItems: "center", gap: "12px", marginBottom: "12px" }}>
<p className="sk-mono sk-label" style={{ margin: "0" }}>DOCUMENTATION</p>
<a href="/changelog" className="sk-version-badge">v1.0.22 — latest</a>
</div>
<h1 className="sk-hero-title">
Automate any<br />browser workflow<br /><span className="sk-hero-accent">with AI.</span>
</h1>
<p className="sk-hero-sub">
Describe what you want in plain English. Skyvern opens a real browser, reads the page visually, and completes the task.
</p>
<div className="sk-hero-actions">
<a href="/getting-started/quickstart" className="sk-btn-primary">Get started with the API →</a>
<a href="/cloud/getting-started/overview" className="sk-btn-secondary">Use the dashboard</a>
</div>
</div>
<div className="sk-hero-right">
<div className="sk-terminal">
<div className="sk-terminal-bar">
<span className="sk-dot sk-dot-red"></span>
<span className="sk-dot sk-dot-yellow"></span>
<span className="sk-dot sk-dot-green"></span>
<span className="sk-terminal-title">skyvern.log</span>
</div>
<div className="sk-terminal-body">
<p className="sk-mono">> Initializing browser session...</p>
<p className="sk-mono sk-t-teal">> Navigating to target URL... [OK]</p>
<p className="sk-mono sk-t-teal">> Analyzing page with vision... [OK]</p>
<p className="sk-mono">> Planning actions...</p>
<p className="sk-mono sk-t-orange">→ ACTIONS_PLANNED: 4</p>
<p className="sk-mono">> Executing: fill_form</p>
<p className="sk-mono">> Executing: click_submit</p>
<p className="sk-mono sk-t-teal">> Task completed successfully</p>
<p className="sk-mono sk-t-muted">→ DURATION: 3.2s</p>
<p className="sk-mono">> READY<span className="sk-cursor">▊</span></p>
</div>
</div>
</div>
</div>
</div>
{/* ===== BENTO GRID — 3 PERSONA LANES ===== */}
<div className="sk-bento-row sk-bento-3col">
<div className="sk-bento-cell">
<p className="sk-mono sk-label">01 — NO CODE</p>
<h3 className="sk-cell-title">Automate without code</h3>
<p className="sk-cell-desc">Use the dashboard to run tasks and build workflows visually.</p>
<div className="sk-cell-links">
<a href="/cloud/getting-started/overview" className="sk-cell-link">Dashboard overview <span className="sk-arrow">↗</span></a>
<a href="/cloud/getting-started/run-your-first-task" className="sk-cell-link">Run your first task <span className="sk-arrow">↗</span></a>
<a href="/cloud/building-workflows/build-a-workflow" className="sk-cell-link">Build a workflow <span className="sk-arrow">↗</span></a>
<a href="/integrations/zapier" className="sk-cell-link">Connect to Zapier <span className="sk-arrow">↗</span></a>
</div>
</div>
<div className="sk-bento-cell">
<p className="sk-mono sk-label">02 — API</p>
<h3 className="sk-cell-title">Build with the API</h3>
<p className="sk-cell-desc">Integrate browser automation into your product with Python, TypeScript, or REST.</p>
<div className="sk-cell-links">
<a href="/getting-started/quickstart" className="sk-cell-link">API quickstart <span className="sk-arrow">↗</span></a>
<a href="/sdk-reference/overview" className="sk-cell-link">Python SDK <span className="sk-arrow">↗</span></a>
<a href="/ts-sdk-reference/overview" className="sk-cell-link">TypeScript SDK <span className="sk-arrow">↗</span></a>
<a href="/api-reference" className="sk-cell-link">API reference <span className="sk-arrow">↗</span></a>
</div>
</div>
<div className="sk-bento-cell">
<p className="sk-mono sk-label">03 — SELF-HOST</p>
<h3 className="sk-cell-title">Self-host</h3>
<p className="sk-cell-desc">Run Skyvern on your own infrastructure with your own LLM keys.</p>
<div className="sk-cell-links">
<a href="/self-hosted/overview" className="sk-cell-link">Deployment overview <span className="sk-arrow">↗</span></a>
<a href="/self-hosted/docker" className="sk-cell-link">Docker setup <span className="sk-arrow">↗</span></a>
<a href="/self-hosted/llm-configuration" className="sk-cell-link">LLM configuration <span className="sk-arrow">↗</span></a>
<a href="/self-hosted/kubernetes" className="sk-cell-link">Kubernetes <span className="sk-arrow">↗</span></a>
</div>
</div>
</div>
{/* ===== BENTO GRID — 2 COL: HOW IT WORKS + CONCEPTS ===== */}
<div className="sk-bento-row sk-bento-2col">
<a href="/getting-started/introduction" className="sk-bento-cell sk-bento-cell-link">
<p className="sk-mono sk-label">ARCHITECTURE</p>
<h3 className="sk-cell-title">How Skyvern works</h3>
<p className="sk-cell-desc">The agent loop, Planner-Agent-Validator architecture, and what happens under the hood when you run a task.</p>
<span className="sk-mono sk-arrow-lg">↗</span>
</a>
<a href="/getting-started/core-concepts" className="sk-bento-cell sk-bento-cell-link">
<p className="sk-mono sk-label">REFERENCE</p>
<h3 className="sk-cell-title">Core concepts</h3>
<p className="sk-cell-desc">Tasks, workflows, blocks, runs, credentials, browser sessions, and engines.</p>
<span className="sk-mono sk-arrow-lg">↗</span>
</a>
</div>
{/* ===== BENTO GRID — 4 COL: USE CASES ===== */}
<p className="sk-mono sk-label" style={{ marginBottom: "24px", marginTop: "32px" }}>USE CASES</p>
<div className="sk-bento-row sk-bento-4col">
<a href="/cookbooks/bulk-invoice-downloader" className="sk-bento-cell sk-bento-cell-link sk-bento-cell-sm sk-usecase-teal">
<h4 className="sk-cell-title-sm">Download invoices</h4>
<p className="sk-cell-desc-sm">Log into vendor portals, find invoices, download PDFs.</p>
<span className="sk-mono sk-arrow-sm">↗</span>
</a>
<a href="/cookbooks/job-application-filler" className="sk-bento-cell sk-bento-cell-link sk-bento-cell-sm sk-usecase-orange">
<h4 className="sk-cell-title-sm">Fill forms at scale</h4>
<p className="sk-cell-desc-sm">Submit applications, registrations, and compliance forms.</p>
<span className="sk-mono sk-arrow-sm">↗</span>
</a>
<a href="/running-automations/extract-structured-data" className="sk-bento-cell sk-bento-cell-link sk-bento-cell-sm sk-usecase-teal">
<h4 className="sk-cell-title-sm">Extract data</h4>
<p className="sk-cell-desc-sm">Pull structured data from any site without an API.</p>
<span className="sk-mono sk-arrow-sm">↗</span>
</a>
<a href="/cookbooks/healthcare-portal-data" className="sk-bento-cell sk-bento-cell-link sk-bento-cell-sm sk-usecase-orange">
<h4 className="sk-cell-title-sm">Healthcare portals</h4>
<p className="sk-cell-desc-sm">Extract patient demographics and billing from EHR portals.</p>
<span className="sk-mono sk-arrow-sm">↗</span>
</a>
</div>
{/* ===== FOOTER CTA ===== */}
<div className="sk-footer-cta">
<div className="sk-footer-cta-inner">
<p className="sk-footer-cta-text">
<span className="sk-mono" style={{ color: "var(--sk-blue)", marginRight: "8px" }}>●</span>
Open source · 20.8k+ GitHub stars
</p>
<div className="sk-footer-cta-links">
<a href="https://github.com/Skyvern-AI/skyvern" className="sk-footer-link">GitHub ↗</a>
<a href="https://discord.gg/skyvern" className="sk-footer-link">Discord ↗</a>
</div>
</div>
</div>
</div>

BIN
docs/logo/dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
docs/logo/light.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View file

@ -0,0 +1,108 @@
export const HomepageHero = () => {
return (
<div
style={{
padding: "48px 0 40px 0",
textAlign: "center",
}}
>
<div
style={{
display: "inline-flex",
alignItems: "center",
gap: "8px",
padding: "6px 16px",
borderRadius: "20px",
backgroundColor: "rgba(91, 138, 140, 0.08)",
border: "1px solid rgba(91, 138, 140, 0.2)",
fontSize: "13px",
color: "#5B8A8C",
marginBottom: "24px",
fontWeight: 500,
}}
>
Open source on GitHub
</div>
<h1
style={{
fontSize: "42px",
fontWeight: 700,
lineHeight: 1.15,
color: "#1a1a1a",
margin: "0 0 16px 0",
letterSpacing: "-0.02em",
}}
>
Automate any browser workflow with AI
</h1>
<p
style={{
fontSize: "18px",
lineHeight: 1.6,
color: "#6B6560",
maxWidth: "600px",
margin: "0 auto 32px auto",
}}
>
Describe what you want in plain English. Skyvern opens a real browser,
reads the page visually, and completes the task.
</p>
<div
style={{
display: "flex",
gap: "12px",
justifyContent: "center",
flexWrap: "wrap",
}}
>
<a
href="/getting-started/quickstart"
style={{
display: "inline-flex",
alignItems: "center",
gap: "6px",
padding: "12px 24px",
borderRadius: "8px",
backgroundColor: "#D4733B",
color: "#fff",
fontSize: "15px",
fontWeight: 600,
textDecoration: "none",
transition: "background-color 0.15s ease",
}}
onMouseEnter={(e) => (e.currentTarget.style.backgroundColor = "#C0632F")}
onMouseLeave={(e) => (e.currentTarget.style.backgroundColor = "#D4733B")}
>
Get started with the API
</a>
<a
href="/cloud/getting-started/overview"
style={{
display: "inline-flex",
alignItems: "center",
gap: "6px",
padding: "12px 24px",
borderRadius: "8px",
backgroundColor: "transparent",
color: "#2C2C2C",
fontSize: "15px",
fontWeight: 600,
textDecoration: "none",
border: "1px solid #E5DDD4",
transition: "all 0.15s ease",
}}
onMouseEnter={(e) => {
e.currentTarget.style.borderColor = "#5B8A8C";
e.currentTarget.style.color = "#5B8A8C";
}}
onMouseLeave={(e) => {
e.currentTarget.style.borderColor = "#E5DDD4";
e.currentTarget.style.color = "#2C2C2C";
}}
>
Use the dashboard
</a>
</div>
</div>
);
};

View file

@ -0,0 +1,118 @@
const Lane = ({ title, description, links }) => {
return (
<div
style={{
padding: "24px",
borderRadius: "12px",
border: "1px solid #E5DDD4",
backgroundColor: "#fff",
transition: "border-color 0.15s ease, box-shadow 0.15s ease",
}}
onMouseEnter={(e) => {
e.currentTarget.style.borderColor = "#7BA3A5";
e.currentTarget.style.boxShadow = "0 4px 12px rgba(0,0,0,0.06)";
}}
onMouseLeave={(e) => {
e.currentTarget.style.borderColor = "#E5DDD4";
e.currentTarget.style.boxShadow = "none";
}}
>
<h3
style={{
fontSize: "16px",
fontWeight: 600,
color: "#1a1a1a",
margin: "0 0 6px 0",
}}
>
{title}
</h3>
<p
style={{
fontSize: "14px",
color: "#6B6560",
margin: "0 0 20px 0",
lineHeight: 1.5,
}}
>
{description}
</p>
<div style={{ display: "flex", flexDirection: "column", gap: "8px" }}>
{links.map((link, i) => (
<a
key={i}
href={link.href}
style={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
padding: "8px 12px",
borderRadius: "6px",
backgroundColor: "rgba(250, 246, 241, 0.6)",
color: "#2C2C2C",
fontSize: "14px",
fontWeight: 500,
textDecoration: "none",
transition: "background-color 0.12s ease, color 0.12s ease",
}}
onMouseEnter={(e) => {
e.currentTarget.style.backgroundColor = "rgba(91, 138, 140, 0.08)";
e.currentTarget.style.color = "#5B8A8C";
}}
onMouseLeave={(e) => {
e.currentTarget.style.backgroundColor = "rgba(250, 246, 241, 0.6)";
e.currentTarget.style.color = "#2C2C2C";
}}
>
<span>{link.label}</span>
<span style={{ fontSize: "12px", opacity: 0.5 }}></span>
</a>
))}
</div>
</div>
);
};
export const PersonaLanes = () => {
return (
<div
style={{
display: "grid",
gridTemplateColumns: "repeat(auto-fit, minmax(260px, 1fr))",
gap: "16px",
margin: "0 0 16px 0",
}}
>
<Lane
title="Automate without code"
description="Use the dashboard to run tasks and build workflows visually."
links={[
{ label: "Dashboard overview", href: "/cloud/getting-started/overview" },
{ label: "Run your first task", href: "/cloud/getting-started/run-your-first-task" },
{ label: "Build a workflow", href: "/cloud/building-workflows/build-a-workflow" },
{ label: "Connect to Zapier", href: "/integrations/zapier" },
]}
/>
<Lane
title="Build with the API"
description="Integrate browser automation into your product with Python, TypeScript, or REST."
links={[
{ label: "API quickstart", href: "/getting-started/quickstart" },
{ label: "Python SDK", href: "/sdk-reference/overview" },
{ label: "TypeScript SDK", href: "/ts-sdk-reference/overview" },
{ label: "API reference", href: "/api-reference" },
]}
/>
<Lane
title="Self-host"
description="Run Skyvern on your own infrastructure with your own LLM keys."
links={[
{ label: "Deployment overview", href: "/self-hosted/overview" },
{ label: "Docker setup", href: "/self-hosted/docker" },
{ label: "LLM configuration", href: "/self-hosted/llm-configuration" },
{ label: "Kubernetes", href: "/self-hosted/kubernetes" },
]}
/>
</div>
);
};

View file

@ -0,0 +1,93 @@
const UseCase = ({ title, description, href }) => {
return (
<a
href={href}
style={{
display: "block",
padding: "20px",
borderRadius: "10px",
border: "1px solid #E5DDD4",
backgroundColor: "#fff",
textDecoration: "none",
color: "inherit",
transition: "border-color 0.15s ease, box-shadow 0.15s ease",
}}
onMouseEnter={(e) => {
e.currentTarget.style.borderColor = "#7BA3A5";
e.currentTarget.style.boxShadow = "0 4px 12px rgba(0,0,0,0.06)";
}}
onMouseLeave={(e) => {
e.currentTarget.style.borderColor = "#E5DDD4";
e.currentTarget.style.boxShadow = "none";
}}
>
<h4
style={{
fontSize: "14px",
fontWeight: 600,
color: "#1a1a1a",
margin: "0 0 4px 0",
}}
>
{title}
</h4>
<p
style={{
fontSize: "13px",
color: "#6B6560",
margin: 0,
lineHeight: 1.5,
}}
>
{description}
</p>
</a>
);
};
export const UseCaseGrid = () => {
return (
<div>
<h3
style={{
fontSize: "15px",
fontWeight: 600,
color: "#6B6560",
textTransform: "uppercase",
letterSpacing: "0.05em",
margin: "0 0 12px 0",
}}
>
Popular use cases
</h3>
<div
style={{
display: "grid",
gridTemplateColumns: "repeat(auto-fit, minmax(220px, 1fr))",
gap: "12px",
}}
>
<UseCase
title="Download invoices"
description="Log into vendor portals, find invoices, download PDFs."
href="/cookbooks/bulk-invoice-downloader"
/>
<UseCase
title="Fill forms at scale"
description="Submit applications, registrations, and compliance forms."
href="/cookbooks/job-application-filler"
/>
<UseCase
title="Extract data from websites"
description="Pull structured data from any site without an API."
href="/running-automations/extract-structured-data"
/>
<UseCase
title="Healthcare portal automation"
description="Extract patient demographics and billing data from EHR portals."
href="/cookbooks/healthcare-portal-data"
/>
</div>
</div>
);
};

651
docs/style.css Normal file
View file

@ -0,0 +1,651 @@
/* ============================================
Skyvern Docs Editorial Bento Grid
Warm brand + structured grid aesthetic
============================================ */
/* --- Brand Tokens (navy product palette + warm landing page) --- */
:root {
--sk-cream: #F8F5EF;
--sk-cream-dark: #EFEBE4;
--sk-navy: #1B2559;
--sk-navy-light: #2B3674;
--sk-navy-faint: #EEF0F8;
--sk-blue: #4A6CF7;
--sk-blue-light: #7B93FA;
--sk-dark: #1B2559;
--sk-dark-hover: #2B3674;
--sk-charcoal: #1B2038;
--sk-text-muted: #68697B;
--sk-border: #D6CEC4;
--sk-border-hover: #4A6CF7;
--sk-shadow: 0 1px 3px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.03);
--sk-shadow-hover: 0 4px 12px rgba(27, 37, 89, 0.1), 0 2px 4px rgba(0, 0, 0, 0.04);
--sk-radius: 10px;
--sk-mono: "JetBrains Mono", "Fira Code", "SF Mono", "Cascadia Code", monospace;
}
/* =============================================
HOMEPAGE Page wrapper (horizontal padding)
============================================= */
.sk-page {
max-width: 1100px;
margin: 0 auto;
padding: 0 40px;
}
@media (max-width: 600px) {
.sk-page {
padding: 0 20px;
}
}
/* =============================================
HOMEPAGE Hero
============================================= */
.sk-hero {
border: 1px solid var(--sk-border);
border-radius: 2px;
margin-bottom: 1px;
position: relative;
overflow: hidden;
}
/* Version badge */
.sk-version-badge {
font-family: var(--sk-mono) !important;
font-size: 10px !important;
letter-spacing: 0.04em !important;
padding: 3px 8px !important;
border-radius: 3px !important;
border: 1px solid var(--sk-border) !important;
color: var(--sk-blue) !important;
text-decoration: none !important;
transition: border-color 0.15s ease !important;
}
.sk-version-badge:hover {
border-color: var(--sk-blue) !important;
}
.sk-hero-inner {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0;
min-height: 380px;
}
.sk-hero-left {
padding: 48px 40px;
display: flex;
flex-direction: column;
justify-content: center;
}
.sk-hero-right {
padding: 32px 40px 32px 0;
display: flex;
align-items: center;
justify-content: center;
border-left: 1px solid var(--sk-border);
}
.sk-hero-title {
font-size: 38px;
font-weight: 700;
line-height: 1.12;
letter-spacing: -0.03em;
margin: 0 0 16px 0;
color: var(--sk-charcoal);
}
.sk-hero-accent {
color: var(--sk-blue);
font-style: italic;
}
.sk-hero-sub {
font-size: 16px;
line-height: 1.6;
color: var(--sk-text-muted);
margin: 0 0 28px 0;
max-width: 440px;
}
.sk-hero-actions {
display: flex;
gap: 12px;
flex-wrap: wrap;
}
/* --- Monospace utility --- */
.sk-mono {
font-family: var(--sk-mono) !important;
font-size: 11px !important;
letter-spacing: 0.04em !important;
}
/* --- Label (section markers like "01 — NO CODE") --- */
.sk-label {
font-family: var(--sk-mono) !important;
font-size: 11px !important;
font-weight: 500 !important;
letter-spacing: 0.08em !important;
text-transform: uppercase !important;
color: var(--sk-text-muted) !important;
margin: 0 0 12px 0 !important;
}
/* --- Terminal block --- */
.sk-terminal {
width: 100%;
max-width: 360px;
border: 1px solid var(--sk-border);
border-radius: 6px;
overflow: hidden;
background: var(--sk-charcoal);
}
.sk-terminal-bar {
display: flex;
align-items: center;
gap: 6px;
padding: 10px 14px;
background: #1a1a1a;
border-bottom: 1px solid #333;
}
.sk-dot {
width: 10px;
height: 10px;
border-radius: 50%;
}
.sk-dot-red { background: #ff5f56; }
.sk-dot-yellow { background: #ffbd2e; }
.sk-dot-green { background: #27c93f; }
.sk-terminal-title {
font-family: var(--sk-mono);
font-size: 11px;
color: #888;
margin-left: 8px;
}
.sk-terminal-body {
padding: 14px 16px;
}
.sk-terminal-body p {
margin: 0 0 4px 0 !important;
font-size: 11.5px !important;
line-height: 1.6 !important;
color: #ccc;
}
.sk-t-teal { color: #7B93FA !important; }
.sk-t-orange { color: #F7C948 !important; }
.sk-t-muted { color: #555 !important; }
/* Blinking cursor */
.sk-cursor {
animation: sk-blink 1s step-end infinite;
color: #7B93FA;
}
@keyframes sk-blink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
/* =============================================
HOMEPAGE Bento Grid System
============================================= */
.sk-bento-row {
display: grid;
gap: 1px;
margin-bottom: 1px;
}
.sk-bento-3col { grid-template-columns: repeat(3, 1fr); }
.sk-bento-2col { grid-template-columns: repeat(2, 1fr); }
.sk-bento-4col { grid-template-columns: repeat(4, 1fr); }
.sk-bento-cell {
border: 1px solid var(--sk-border);
border-radius: 2px;
padding: 28px 24px;
background: transparent;
position: relative;
transition: background-color 0.15s ease;
}
.sk-bento-cell:hover {
background-color: rgba(74, 108, 247, 0.03);
}
/* Linked cells */
a.sk-bento-cell-link {
text-decoration: none !important;
color: inherit !important;
display: block;
}
a.sk-bento-cell-link:hover {
background-color: rgba(74, 108, 247, 0.04);
}
a.sk-bento-cell-link:hover .sk-arrow-lg,
a.sk-bento-cell-link:hover .sk-arrow-sm {
color: var(--sk-blue) !important;
}
/* --- Cell typography --- */
.sk-cell-title {
font-size: 17px;
font-weight: 600;
margin: 0 0 6px 0;
color: var(--sk-charcoal);
}
.sk-cell-desc {
font-size: 14px;
color: var(--sk-text-muted);
margin: 0 0 20px 0;
line-height: 1.5;
}
.sk-cell-title-sm {
font-size: 14px;
font-weight: 600;
margin: 0 0 4px 0;
color: var(--sk-charcoal);
}
.sk-cell-desc-sm {
font-size: 13px;
color: var(--sk-text-muted);
margin: 0;
line-height: 1.5;
}
.sk-bento-cell-sm {
padding: 20px;
}
/* --- Links inside cells --- */
.sk-cell-links {
display: flex;
flex-direction: column;
gap: 2px;
}
.sk-cell-link {
display: flex;
align-items: center;
justify-content: space-between;
padding: 7px 10px;
font-size: 13.5px;
font-weight: 500;
color: var(--sk-charcoal) !important;
text-decoration: none !important;
border-radius: 3px;
transition: background-color 0.12s ease, color 0.12s ease;
border-bottom: none !important;
}
.sk-cell-link:hover {
background-color: rgba(74, 108, 247, 0.06);
color: var(--sk-blue) !important;
}
/* --- Arrows --- */
.sk-arrow {
font-family: var(--sk-mono);
font-size: 12px;
opacity: 0.35;
transition: opacity 0.12s ease, color 0.12s ease;
}
.sk-cell-link:hover .sk-arrow {
opacity: 1;
color: var(--sk-blue);
}
.sk-arrow-lg {
font-family: var(--sk-mono) !important;
font-size: 18px !important;
position: absolute;
top: 24px;
right: 24px;
opacity: 0.25;
transition: opacity 0.15s ease;
color: var(--sk-text-muted) !important;
}
.sk-arrow-sm {
font-family: var(--sk-mono) !important;
font-size: 14px !important;
position: absolute;
top: 16px;
right: 16px;
opacity: 0.25;
transition: opacity 0.15s ease;
color: var(--sk-text-muted) !important;
}
/* =============================================
HOMEPAGE Buttons
============================================= */
.sk-btn-primary {
display: inline-flex !important;
align-items: center !important;
padding: 11px 22px !important;
border-radius: 6px !important;
background-color: var(--sk-dark) !important;
color: #fff !important;
font-size: 14px !important;
font-weight: 600 !important;
text-decoration: none !important;
transition: background-color 0.15s ease !important;
border: none !important;
}
.sk-btn-primary:hover {
background-color: var(--sk-dark-hover) !important;
}
.sk-btn-secondary {
display: inline-flex !important;
align-items: center !important;
padding: 11px 22px !important;
border-radius: 4px !important;
background-color: transparent !important;
color: var(--sk-charcoal) !important;
font-size: 14px !important;
font-weight: 600 !important;
text-decoration: none !important;
border: 1px solid var(--sk-border) !important;
transition: border-color 0.15s ease, color 0.15s ease !important;
}
.sk-btn-secondary:hover {
border-color: var(--sk-blue) !important;
color: var(--sk-charcoal) !important;
}
/* --- Use case left-border accents --- */
.sk-usecase-teal {
border-left: 3px solid var(--sk-blue) !important;
}
.sk-usecase-orange {
border-left: 3px solid var(--sk-dark) !important;
}
/* =============================================
HOMEPAGE Footer CTA
============================================= */
.sk-footer-cta {
border: 1px solid var(--sk-border);
border-radius: 2px;
margin-top: 1px;
padding: 20px 24px;
}
.sk-footer-cta-inner {
display: flex;
align-items: center;
justify-content: space-between;
}
.sk-footer-cta-text {
font-size: 14px;
font-weight: 500;
color: var(--sk-charcoal);
margin: 0;
}
.sk-footer-cta-links {
display: flex;
gap: 16px;
}
.sk-footer-link {
font-family: var(--sk-mono) !important;
font-size: 12px !important;
letter-spacing: 0.04em !important;
color: var(--sk-text-muted) !important;
text-decoration: none !important;
transition: color 0.15s ease !important;
border-bottom: none !important;
}
.sk-footer-link:hover {
color: var(--sk-blue) !important;
}
@media (max-width: 600px) {
.sk-footer-cta-inner {
flex-direction: column;
gap: 12px;
text-align: center;
}
}
/* =============================================
RESPONSIVE
============================================= */
@media (max-width: 900px) {
.sk-hero-inner {
grid-template-columns: 1fr;
}
.sk-hero-right {
border-left: none;
border-top: 1px solid var(--sk-border);
padding: 24px;
}
.sk-bento-3col {
grid-template-columns: 1fr;
}
.sk-bento-4col {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 600px) {
.sk-bento-2col,
.sk-bento-4col {
grid-template-columns: 1fr;
}
.sk-hero-left {
padding: 32px 24px;
}
.sk-hero-title {
font-size: 28px;
}
}
/* =============================================
EXISTING Cards, Tables, Links, etc.
(non-homepage styles)
============================================= */
[class*="Card"],
.card {
border: 1px solid var(--sk-border) !important;
border-radius: 12px !important;
box-shadow: var(--sk-shadow) !important;
transition: box-shadow 0.2s ease, border-color 0.2s ease !important;
}
[class*="Card"]:hover,
.card:hover {
box-shadow: var(--sk-shadow-hover) !important;
border-color: var(--sk-border-hover) !important;
}
article img {
border: 1px solid var(--sk-border) !important;
border-radius: var(--sk-radius) !important;
box-shadow: var(--sk-shadow) !important;
}
table {
border-color: var(--sk-border) !important;
border-radius: var(--sk-radius) !important;
overflow: hidden;
}
th {
background-color: var(--sk-cream-dark) !important;
border-color: var(--sk-border) !important;
color: var(--sk-charcoal) !important;
font-weight: 600 !important;
}
td {
border-color: var(--sk-border) !important;
}
tr:nth-child(even) td {
background-color: rgba(250, 246, 241, 0.5) !important;
}
article a:not([class]) {
color: var(--sk-blue) !important;
text-decoration: none !important;
border-bottom: 1px solid transparent !important;
transition: color 0.15s ease, border-color 0.15s ease !important;
}
article a:not([class]):hover {
color: var(--sk-dark) !important;
border-bottom-color: var(--sk-dark) !important;
}
[class*="callout"],
[class*="Callout"],
[class*="note"],
[class*="Note"] {
border-left-color: var(--sk-blue) !important;
border-radius: 0 var(--sk-radius) var(--sk-radius) 0 !important;
background-color: var(--sk-navy-faint) !important;
}
[class*="warning"],
[class*="Warning"] {
border-left-color: #E5A236 !important;
background-color: #FEF9EE !important;
}
[class*="Accordion"],
[class*="accordion"],
details {
border-color: var(--sk-border) !important;
border-radius: var(--sk-radius) !important;
}
hr {
border: none !important;
height: 1px !important;
background: linear-gradient(90deg, transparent 0%, var(--sk-blue-light) 20%, var(--sk-blue) 50%, var(--sk-blue-light) 80%, transparent 100%) !important;
opacity: 0.3 !important;
margin: 2rem 0 !important;
}
[class*="Step"] [class*="number"],
[class*="step"] [class*="number"],
[class*="Steps"] [class*="indicator"] {
background-color: var(--sk-blue) !important;
color: white !important;
}
[data-active="true"],
[aria-current="page"] {
color: var(--sk-blue) !important;
}
#navbar,
nav[class*="Navbar"],
header[class*="header"] {
border-bottom: 1px solid var(--sk-border) !important;
}
::selection {
background-color: rgba(74, 108, 247, 0.15);
}
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track {
background: var(--sk-cream);
}
::-webkit-scrollbar-thumb {
background: var(--sk-border);
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--sk-blue-light);
}
/* =============================================
DARK MODE
============================================= */
.dark,
[data-theme="dark"] {
--sk-cream: #1C1917;
--sk-cream-dark: #252220;
--sk-navy-faint: rgba(74, 108, 247, 0.08);
--sk-dark: #7B93FA;
--sk-dark-hover: #93A7FB;
--sk-charcoal: #F5F0EB;
--sk-text-muted: #A8A29E;
--sk-border: #2E2A26;
--sk-border-hover: #4A6CF7;
--sk-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
--sk-shadow-hover: 0 4px 12px rgba(0, 0, 0, 0.3);
}
.dark .sk-terminal,
[data-theme="dark"] .sk-terminal {
border-color: #333;
}
.dark .sk-cell-link:hover,
[data-theme="dark"] .sk-cell-link:hover {
background-color: rgba(74, 108, 247, 0.1);
}
.dark tr:nth-child(even) td,
[data-theme="dark"] tr:nth-child(even) td {
background-color: rgba(28, 25, 23, 0.5) !important;
}
.dark ::-webkit-scrollbar-track,
[data-theme="dark"] ::-webkit-scrollbar-track {
background: #1C1917;
}