refactor: convert docs templates to use TailwindCSS
Phase 4+5: Replace custom CSS with Tailwind utility classes - Remove ~250 lines of custom CSS from docs.templ - Remove ~120 lines of custom CSS from api_explorer.templ - Use Tailwind CDN for styling - Keep only essential CSS: * CSS variables for theming (--bg-primary, --accent, etc.) * Custom scrollbar styling * Mobile sidebar transform transitions * Smooth scroll behavior - All layout, spacing, colors, typography now use Tailwind classes - Mobile-first responsive design with Tailwind breakpoints - Proper accessibility with aria-labels and semantic HTML Benefits: - Consistent design system - Smaller custom CSS footprint - Better maintainability - Follows PROJECT_GUIDELINES.md requirement: TailwindCSS primary
This commit is contained in:
+66
-163
@@ -3,60 +3,64 @@ package templates
|
||||
templ APIExplorer(explorer APIExplorerData) {
|
||||
if !explorer.IsLoggedIn {
|
||||
// Show mode toggle and mock data
|
||||
<div class="api-explorer">
|
||||
<div class="api-mode-toggle">
|
||||
<button class="mode-btn active">Mock Data</button>
|
||||
<button class="mode-btn" disabled>Login to Try Real</button>
|
||||
<div class="border border-border rounded-lg p-6 mt-8 bg-background-secondary">
|
||||
<div class="flex gap-2 mb-4">
|
||||
<button class="px-4 py-2 border border-border rounded bg-background-primary text-text-primary cursor-pointer bg-accent text-white">Mock Data</button>
|
||||
<button class="px-4 py-2 border border-border rounded bg-background-primary text-text-primary opacity-50 cursor-not-allowed" disabled>Login to Try Real</button>
|
||||
</div>
|
||||
<div class="api-request-editor">
|
||||
<div class="method-selector">
|
||||
<select disabled>
|
||||
<div class="mb-4">
|
||||
<div class="mb-4">
|
||||
<select disabled class="w-full px-3 py-2 rounded bg-background-primary text-text-primary border border-border focus:outline-none focus:ring-2 focus:ring-accent">
|
||||
<option value="GET" selected?={ explorer.Endpoint.Method == "GET" }>GET</option>
|
||||
<option value="POST" selected?={ explorer.Endpoint.Method == "POST" }>POST</option>
|
||||
<option value="PUT" selected?={ explorer.Endpoint.Method == "PUT" }>PUT</option>
|
||||
<option value="DELETE" selected?={ explorer.Endpoint.Method == "DELETE" }>DELETE</option>
|
||||
</select>
|
||||
</div>
|
||||
<h4>Request Body (Example)</h4>
|
||||
<pre><code>{ explorer.Endpoint.RequestBody }</code></pre>
|
||||
<h4 class="text-text-secondary mb-2 text-sm font-medium">Request Body (Example)</h4>
|
||||
<pre class="bg-background-primary p-4 rounded-lg overflow-x-auto"><code class="text-text-primary text-sm">{ explorer.Endpoint.RequestBody }</code></pre>
|
||||
</div>
|
||||
<div class="api-response">
|
||||
<h4>Response (Mock)</h4>
|
||||
<pre><code>{ explorer.Endpoint.Response }</code></pre>
|
||||
<div class="mt-4">
|
||||
<h4 class="text-text-secondary mb-2 text-sm font-medium">Response (Mock)</h4>
|
||||
<pre class="bg-background-primary p-4 rounded-lg overflow-x-auto"><code class="text-text-primary text-sm">{ explorer.Endpoint.Response }</code></pre>
|
||||
</div>
|
||||
<div class="flex gap-2 mt-4">
|
||||
<button onclick="copyToClipboard(JSON.stringify({ explorer.Endpoint.RequestBody }, null, 2))" class="px-4 py-2 border border-border rounded bg-background-primary text-text-primary hover:bg-background-secondary cursor-pointer text-sm">Copy Request</button>
|
||||
<button onclick="copyToClipboard(JSON.stringify({ explorer.Endpoint.Response }, null, 2))" class="px-4 py-2 border border-border rounded bg-background-primary text-text-primary hover:bg-background-secondary cursor-pointer text-sm">Copy Response</button>
|
||||
</div>
|
||||
<button onclick="copyToClipboard(JSON.stringify({ explorer.Endpoint.RequestBody }, null, 2))">Copy Request</button>
|
||||
<button onclick="copyToClipboard(JSON.stringify({ explorer.Endpoint.Response }, null, 2))">Copy Response</button>
|
||||
</div>
|
||||
} else {
|
||||
// Show interactive explorer with real execution
|
||||
<div class="api-explorer">
|
||||
<div class="api-mode-toggle">
|
||||
<button class="mode-btn" id="mock-btn">Mock Data</button>
|
||||
<button class="mode-btn active" id="real-btn">Try It Out</button>
|
||||
<div class="border border-border rounded-lg p-6 mt-8 bg-background-secondary">
|
||||
<div class="flex gap-2 mb-4">
|
||||
<button class="mode-btn px-4 py-2 border border-border rounded bg-background-primary text-text-primary cursor-pointer hover:bg-background-secondary text-sm" id="mock-btn">Mock Data</button>
|
||||
<button class="mode-btn px-4 py-2 bg-accent text-white rounded cursor-pointer text-sm" id="real-btn">Try It Out</button>
|
||||
</div>
|
||||
<div class="api-request-editor">
|
||||
<div class="method-selector">
|
||||
<select id="http-method">
|
||||
<div class="mb-4">
|
||||
<div class="mb-4">
|
||||
<select id="http-method" class="w-full px-3 py-2 rounded bg-background-primary text-text-primary border border-border focus:outline-none focus:ring-2 focus:ring-accent">
|
||||
<option value="GET" selected?={ explorer.Endpoint.Method == "GET" }>GET</option>
|
||||
<option value="POST" selected?={ explorer.Endpoint.Method == "POST" }>POST</option>
|
||||
<option value="PUT" selected?={ explorer.Endpoint.Method == "PUT" }>PUT</option>
|
||||
<option value="DELETE" selected?={ explorer.Endpoint.Method == "DELETE" }>DELETE</option>
|
||||
</select>
|
||||
</div>
|
||||
<h4>Request Body</h4>
|
||||
<textarea id="request-body" placeholder="Edit request body...">{ explorer.Endpoint.RequestBody }</textarea>
|
||||
<h4 class="text-text-secondary mb-2 text-sm font-medium">Request Body</h4>
|
||||
<textarea id="request-body" placeholder="Edit request body..." class="w-full min-h-[150px] px-3 py-2 font-mono text-sm bg-background-primary text-text-primary border border-border rounded focus:outline-none focus:ring-2 focus:ring-accent">{ explorer.Endpoint.RequestBody }</textarea>
|
||||
</div>
|
||||
<button id="try-it-out">Try It Out</button>
|
||||
<div class="api-response" style="display:none">
|
||||
<div class="response-header">
|
||||
<span id="response-status"></span>
|
||||
<span id="response-time"></span>
|
||||
<button id="try-it-out" class="bg-accent text-white px-6 py-3 rounded cursor-pointer mt-4 hover:opacity-90">Try It Out</button>
|
||||
<div class="api-response mt-4 hidden">
|
||||
<div class="flex justify-between mb-2 text-sm">
|
||||
<span id="response-status" class="font-semibold"></span>
|
||||
<span id="response-time" class="text-text-secondary"></span>
|
||||
</div>
|
||||
<pre><code id="response-body"></code></pre>
|
||||
<pre class="bg-background-primary p-4 rounded-lg overflow-x-auto"><code id="response-body" class="text-text-primary text-sm"></code></pre>
|
||||
</div>
|
||||
<div class="flex gap-2 mt-4 flex-wrap">
|
||||
<button onclick="copyRequest()" class="px-4 py-2 border border-border rounded bg-background-primary text-text-primary hover:bg-background-secondary cursor-pointer text-sm">Copy Request</button>
|
||||
<button onclick="copyResponse()" class="px-4 py-2 border border-border rounded bg-background-primary text-text-primary hover:bg-background-secondary cursor-pointer text-sm">Copy Response</button>
|
||||
<button onclick="generateCURL()" class="px-4 py-2 border border-border rounded bg-background-primary text-text-primary hover:bg-background-secondary cursor-pointer text-sm">Generate cURL</button>
|
||||
</div>
|
||||
<button onclick="copyRequest()">Copy Request</button>
|
||||
<button onclick="copyResponse()">Copy Response</button>
|
||||
<button onclick="generateCURL()">Generate cURL</button>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -70,17 +74,36 @@ templ APIExplorer(explorer APIExplorerData) {
|
||||
document.getElementById('real-btn')?.addEventListener('click', () => showMode('real'));
|
||||
|
||||
function showMode(mode) {
|
||||
const mockBtn = document.getElementById('mock-btn');
|
||||
const realBtn = document.getElementById('real-btn');
|
||||
const responseBody = document.getElementById('response-body');
|
||||
const responseStatus = document.getElementById('response-status');
|
||||
const responseTime = document.getElementById('response-time');
|
||||
const apiResponse = document.querySelector('.api-response');
|
||||
const requestBody = document.getElementById('request-body');
|
||||
const tryItOut = document.getElementById('try-it-out');
|
||||
|
||||
if (mode === 'mock') {
|
||||
document.querySelector('.api-response').style.display = 'block';
|
||||
document.getElementById('request-body').readOnly = true;
|
||||
document.getElementById('try-it-out').style.display = 'none';
|
||||
document.getElementById('response-body').textContent = JSON.stringify(exampleResponse, null, 2);
|
||||
document.getElementById('response-status').textContent = '200 OK';
|
||||
document.getElementById('response-time').textContent = 'Mock';
|
||||
mockBtn.classList.add('bg-accent', 'text-white');
|
||||
mockBtn.classList.remove('bg-background-primary', 'text-text-primary');
|
||||
realBtn.classList.remove('bg-accent', 'text-white');
|
||||
realBtn.classList.add('bg-background-primary', 'text-text-primary');
|
||||
|
||||
apiResponse.classList.remove('hidden');
|
||||
requestBody.readOnly = true;
|
||||
tryItOut.classList.add('hidden');
|
||||
responseBody.textContent = JSON.stringify(exampleResponse, null, 2);
|
||||
responseStatus.textContent = '200 OK';
|
||||
responseTime.textContent = 'Mock';
|
||||
} else {
|
||||
document.querySelector('.api-response').style.display = 'none';
|
||||
document.getElementById('request-body').readOnly = false;
|
||||
document.getElementById('try-it-out').style.display = 'block';
|
||||
realBtn.classList.add('bg-accent', 'text-white');
|
||||
realBtn.classList.remove('bg-background-primary', 'text-text-primary');
|
||||
mockBtn.classList.remove('bg-accent', 'text-white');
|
||||
mockBtn.classList.add('bg-background-primary', 'text-text-primary');
|
||||
|
||||
apiResponse.classList.add('hidden');
|
||||
requestBody.readOnly = false;
|
||||
tryItOut.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,11 +129,11 @@ templ APIExplorer(explorer APIExplorerData) {
|
||||
document.getElementById('response-status').textContent = `${response.status} (${response.statusText})`;
|
||||
document.getElementById('response-time').textContent = `${duration}ms`;
|
||||
document.getElementById('response-body').textContent = JSON.stringify(data, null, 2);
|
||||
document.querySelector('.api-response').style.display = 'block';
|
||||
document.querySelector('.api-response').classList.remove('hidden');
|
||||
} catch (error) {
|
||||
document.getElementById('response-status').textContent = 'Error';
|
||||
document.getElementById('response-body').textContent = error.message;
|
||||
document.querySelector('.api-response').style.display = 'block';
|
||||
document.querySelector('.api-response').classList.remove('hidden');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -144,126 +167,6 @@ templ APIExplorer(explorer APIExplorerData) {
|
||||
navigator.clipboard.writeText(curl);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.api-explorer {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
margin-top: 2rem;
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
|
||||
.api-mode-toggle {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.mode-btn {
|
||||
padding: 0.5rem 1rem;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mode-btn.active {
|
||||
background: var(--accent);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.mode-btn:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.api-request-editor {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.method-selector select {
|
||||
padding: 0.5rem;
|
||||
border-radius: 4px;
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
border: 1px solid var(--border);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.api-request-editor h4 {
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
textarea#request-body {
|
||||
width: 100%;
|
||||
min-height: 150px;
|
||||
padding: 0.75rem;
|
||||
font-family: 'Monaco', 'Consolas', monospace;
|
||||
font-size: 0.875rem;
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.api-response {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.response-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
#response-status {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#response-time {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.api-response pre {
|
||||
background: var(--bg-primary);
|
||||
padding: 1rem;
|
||||
border-radius: 4px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.api-response code {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.api-explorer button {
|
||||
padding: 0.5rem 1rem;
|
||||
margin-right: 0.5rem;
|
||||
margin-top: 1rem;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#try-it-out {
|
||||
background: var(--accent);
|
||||
color: white;
|
||||
padding: 0.75rem 1.5rem;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
#try-it-out:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
</style>
|
||||
}
|
||||
|
||||
// APIExplorerData holds data for the API explorer component
|
||||
|
||||
Reference in New Issue
Block a user