Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,42 @@ Copy the asset url and update the base url to "https://static.corecollective.dev
Add the url in alphabetical order to the company.yaml list with a `alt` field also.
If the logo appears larger or small add a `scale` field also to adjust accordingly.

## Updating FAQ content

FAQ entries are stored in `src/content/faq/faq.yaml`. Each entry has a `question` and an `answer` field.

### Simple answers

For short, plain-text answers:

```yaml
- question: "What is CoreCollective?"
answer: "CoreCollective provides a neutral platform for collaboration in the Arm ecosystem."
```

### Rich answers with HTML

For answers that need bullet lists, links, email addresses, or multiple paragraphs, use the YAML `>` folded block scalar and write HTML:

```yaml
- question: "What is the process for requesting a new Working Group?"
answer: >
<p>CoreCollective is always open to proposals. The process involves:</p>
<ul>
<li>Submit a proposal to the TAC</li>
<li>Present to the TAC for approval</li>
</ul>
<p>Contact <a href="mailto:info@corecollective.dev">info@corecollective.dev</a> for details.</p>
```

**Supported HTML tags:** `<p>`, `<ul>`, `<ol>`, `<li>`, `<a href="...">`, `<strong>`, `<em>`

**Tips:**
- Always wrap paragraphs in `<p>` tags when using multiple paragraphs
- Use `<a href="mailto:...">` for email addresses
- Links are automatically styled in cyan; lists and paragraphs are styled via Tailwind Typography
- Preview changes locally with `npm run dev` before deploying

## Updating blogs

New blogs should be added as `.mdx` files to `src/content/blogs` folder.
Expand Down
176 changes: 158 additions & 18 deletions src/components/nav/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useRef } from 'react';
import LogoImg from '../../assets/CoreCollective_White-Wordmark.png';

const navLinks = [
type NavLink =
| { name: string; href: string }
| { name: string; children: { name: string; href: string }[] };

const navLinks: NavLink[] = [
{ name: 'Working Groups', href: '/working-groups/' },
{ name: 'About', href: '/about/' },
{
name: 'About',
children: [
{ name: 'About', href: '/about/' },
{ name: 'FAQ', href: '/faq/' },
],
},
{ name: 'Join', href: '/join/' },
{ name: 'Blog & News', href: '/blog/' },
{ name: 'Contact', href: '/contact/' },
Expand All @@ -12,11 +22,26 @@ const navLinks = [
export default function Navbar() {
const [isOpen, setIsOpen] = useState(false);
const [currentPath, setCurrentPath] = useState('');
const [dropdownOpen, setDropdownOpen] = useState<string | null>(null);
const dropdownRef = useRef<HTMLLIElement>(null);

useEffect(() => {
setCurrentPath(globalThis.location.pathname);
}, []);

useEffect(() => {
function handleClickOutside(e: MouseEvent) {
if (
dropdownRef.current &&
!dropdownRef.current.contains(e.target as Node)
) {
setDropdownOpen(null);
}
}
document.addEventListener('click', handleClickOutside);
return () => document.removeEventListener('click', handleClickOutside);
}, []);

return (
<>
<header className="bg-cc-blue fixed top-0 z-50 h-30 w-full text-white">
Expand All @@ -28,6 +53,63 @@ export default function Navbar() {
</div>
<ul className="hidden items-center md:flex">
{navLinks.map((link) => {
if ('children' in link) {
const isActive = link.children.some(
(child) => currentPath === child.href,
);
const isDropdownOpen = dropdownOpen === link.name;
return (
<li
key={link.name}
ref={dropdownRef}
className="border-cc-cyan relative flex h-6 items-center border-r-2 px-6 first:border-l-2 last:border-r-0"
>
<button
onClick={() =>
setDropdownOpen(isDropdownOpen ? null : link.name)
}
className={`hover:text-cc-cyan flex items-center gap-1 transition-colors ${
isActive ? 'text-cc-cyan font-bold' : 'text-white'
}`}
>
{link.name}
<svg
className={`h-3 w-3 transition-transform ${isDropdownOpen ? 'rotate-180' : ''}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M19 9l-7 7-7-7"
/>
</svg>
</button>
{isDropdownOpen && (
<ul className="bg-cc-blue absolute top-full left-0 mt-3 min-w-40 rounded-md border border-white/10 py-2 shadow-lg">
{link.children.map((child) => (
<li key={child.name}>
<a
href={child.href}
className={`hover:text-cc-cyan block px-4 py-2 transition-colors ${
currentPath === child.href
? 'text-cc-cyan font-bold'
: 'text-white'
}`}
onClick={() => setDropdownOpen(null)}
>
{child.name}
</a>
</li>
))}
</ul>
)}
</li>
);
}

const isActive =
currentPath === link.href ||
(currentPath === '' && link.href === '/');
Expand Down Expand Up @@ -82,22 +164,80 @@ export default function Navbar() {
className={`${isOpen ? 'block' : 'hidden'} bg-cc-blue absolute left-0 w-full border-t border-white/10 shadow-lg md:hidden`}
>
<ul className="flex flex-col p-4">
{navLinks.map((link) => (
<li
key={link.name}
className="border-b border-white/10 last:border-b-0"
>
<a
href={link.href}
className={`hover:text-cc-cyan block py-4 ${
currentPath === link.href ? 'text-cc-cyan' : 'text-white'
}`}
onClick={() => setIsOpen(false)}
{navLinks.map((link) => {
if ('children' in link) {
const isMobileDropdownOpen = dropdownOpen === link.name;
return (
<li
key={link.name}
className="border-b border-white/10 last:border-b-0"
>
<button
onClick={() =>
setDropdownOpen(
isMobileDropdownOpen ? null : link.name,
)
}
className="hover:text-cc-cyan flex w-full items-center justify-between py-4 text-white"
>
{link.name}
<svg
className={`h-3 w-3 transition-transform ${isMobileDropdownOpen ? 'rotate-180' : ''}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M19 9l-7 7-7-7"
/>
</svg>
</button>
{isMobileDropdownOpen && (
<ul className="pb-2 pl-4">
{link.children.map((child) => (
<li key={child.name}>
<a
href={child.href}
className={`hover:text-cc-cyan block py-2 ${
currentPath === child.href
? 'text-cc-cyan'
: 'text-white'
}`}
onClick={() => {
setIsOpen(false);
setDropdownOpen(null);
}}
>
{child.name}
</a>
</li>
))}
</ul>
)}
</li>
);
}

return (
<li
key={link.name}
className="border-b border-white/10 last:border-b-0"
>
{link.name}
</a>
</li>
))}
<a
href={link.href}
className={`hover:text-cc-cyan block py-4 ${
currentPath === link.href ? 'text-cc-cyan' : 'text-white'
}`}
onClick={() => setIsOpen(false)}
>
{link.name}
</a>
</li>
);
})}
</ul>
</div>
</header>
Expand Down
11 changes: 11 additions & 0 deletions src/content/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,19 @@ const leadershipCollection = defineCollection({
),
});

const faqCollection = defineCollection({
type: 'data',
schema: z.array(
z.object({
question: z.string(),
answer: z.string(),
})
),
});

export const collections = {
blogs,
logos: logosCollection,
leadership: leadershipCollection,
faq: faqCollection,
};
14 changes: 14 additions & 0 deletions src/content/faq/faq.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
- question: "Is there a clear role and set of responsibilities for Linaro?"
answer: >
Linaro is an active Member of CoreCollective and provides governance, hosting, and other services.

- question: "What is the process for requesting the creation of a new CoreCollective Working Group?"
answer: >
<p>CoreCollective is always open to the proposal of new Working Groups. A new WG must be approved by the TAC Chair / TAC.</p>
<p>Considerations regarding requesting the launch of new WGs:</p>
<ul>
<li>Each new group will require a WG Chair—someone to help define the WG and lead member outreach. When requesting a new Working Group, the requester should be willing to recommend a candidate who is willing to serve as the Working Group Chair.</li>
<li>Provide an outline of the WG'S scope. A summary of expectations, goals, focus, etc. are needed</li>
<li>Providing a list of recommended WG member companies and a willingness to approach them is essential for kicking off a new WG.</li>
</ul>
<p>The above can be sent to <a href="mailto:enquiries@corecollective.dev">enquiries@corecollective.dev</a> for consideration. A member of the leadership team will respond with additional questions and proposed next steps.</p>
50 changes: 50 additions & 0 deletions src/pages/faq/index.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
import BaseLayout from '../../layouts/BaseLayout.astro';
import backgroundImage from '../../assets/0233f2c4c9a0817f3a88064a357678647bb97e85.png';
import { getEntry } from 'astro:content';

const faqData = await getEntry('faq', 'faq');
const faqs = faqData?.data || [];
---

<BaseLayout title="FAQ">
<div
class="flex w-full flex-col items-center gap-16 py-14 md:px-32"
style={{
backgroundImage: `url(${backgroundImage.src})`,
backgroundSize: 'cover',
backgroundPosition: 'center',
}}
>
<h1 class="text-center text-3xl font-bold text-white md:text-5xl">
Frequently Asked Questions
</h1>
<div class="mx-4 flex w-full max-w-4xl flex-col gap-4 text-left md:mx-0">
{
faqs.map((faq) => (
<details class="group rounded-lg border border-white/10 bg-white/5">
<summary class="flex cursor-pointer items-center justify-between px-6 py-5 text-lg font-semibold text-white transition-colors hover:text-cc-cyan md:text-xl [&::-webkit-details-marker]:hidden">
<span>{faq.question}</span>
<svg
class="h-5 w-5 shrink-0 transition-transform group-open:rotate-180"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M19 9l-7 7-7-7"
/>
</svg>
</summary>
<div class="prose prose-invert prose-lg max-w-none px-6 pb-5 font-light text-white/80 prose-a:text-cc-cyan prose-li:text-white/80">
<Fragment set:html={faq.answer} />
</div>
</details>
))
}
</div>
</div>
</BaseLayout>