Skip to content
Open
161 changes: 161 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ We really want to make space for all developers to feel comfortable and supporte
- [Tips and Gotchas](https://github.com/Virtual-Coffee/virtualcoffee.io/blob/main/CONTRIBUTING.md#tips-and-gotchas)
- [Reporting A Bug](https://github.com/Virtual-Coffee/virtualcoffee.io/blob/main/CONTRIBUTING.md#reporting-a-bug)
- [Labelling conventions](https://github.com/Virtual-Coffee/virtualcoffee.io/blob/main/CONTRIBUTING.md#labelling-conventions)
- [Content Tagging System](https://github.com/Virtual-Coffee/virtualcoffee.io/blob/main/CONTRIBUTING.md#content-tagging-system)

## What Type of Contributions We're Looking For

Expand Down Expand Up @@ -161,6 +162,166 @@ This repository has three basic types of labels:
- `Status` - What part of the process is this issue in. e.g. Active, Needs Review, Resolved, etc. (will be filled in by maintainers and some contributors). Only one active per issue.
- `Context` - Additional info that helps people parse issues. e.g. "good first issue", "for maintainer only". Multiple may be active on one issue.

## Content Tagging System

Our resource filtering system uses a structured tagging approach to help users find relevant content quickly. All content tags are organized into logical categories to improve discoverability and user experience.

### Tag Categories

#### 🎯 Skill Level

Tags that indicate the experience level required for the resource:

- **`beginner`** - Content suitable for those new to programming or the specific topic
- **`intermediate`** - Content for developers with some experience who want to deepen their knowledge
- **`advanced`** - Content for experienced developers looking to master complex topics

#### 🛠️ Technologies

Tags for programming languages, frameworks, and tools:

- **`javascript`** - JavaScript language, ES6+, Node.js, and related concepts
- **`typescript`** - TypeScript language, type systems, and TypeScript-specific patterns
- **`react`** - React library, hooks, components, and React ecosystem
- **`node`** - Node.js runtime, server-side JavaScript, and backend development
- **`css`** - CSS styling, layouts, animations, and modern CSS features
- **`html`** - HTML markup, semantic elements, and web standards
- **`git`** - Git version control, branching strategies, and collaboration workflows

#### 📚 Topics

Subject areas and domains:

- **`career`** - Career development, job searching, professional growth, and industry insights
- **`open-source`** - Contributing to open source, maintaining projects, and community involvement
- **`testing`** - Testing strategies, frameworks, TDD, and quality assurance
- **`deployment`** - Deployment strategies, CI/CD, hosting, and DevOps practices
- **`interviewing`** - Technical interviews, coding challenges, and interview preparation

#### 📖 Format

Type of content or learning material:

- **`tutorial`** - Step-by-step instructional content with hands-on examples
- **`reference`** - Quick lookup guides, documentation, and technical specifications
- **`guide`** - Comprehensive walkthroughs and best practices
- **`tips`** - Short, actionable advice and quick wins

### Tagging Guidelines

#### When to Add New Tags

**Add a new tag when:**

- A new technology, framework, or tool becomes widely adopted in the community
- A new topic area emerges that doesn't fit existing categories
- Multiple resources would benefit from a more specific tag than existing ones
- The tag would help users find content more effectively

**Don't add a new tag when:**

- Only one or two resources would use the tag
- An existing tag already covers the concept adequately
- The tag is too specific or niche for general use
- The tag duplicates functionality of existing tags

#### How to Add New Tags

1. **Propose the tag** - Create an issue or discussion explaining:
- What the new tag represents
- Which category it belongs to
- Why existing tags don't suffice
- Examples of content that would use this tag

2. **Get community feedback** - Allow time for maintainers and community members to discuss the proposal

3. **Update the system** - Once approved, the tag will be added to the `TAG_CATEGORIES` configuration in `src/util/tagCategories.ts`

#### Tagging Best Practices

**Do:**

- Use existing tags when they accurately describe your content
- Apply multiple relevant tags from different categories
- Be consistent with tag naming (lowercase, hyphenated for multi-word tags)
- Consider your audience's skill level when choosing skill-level tags
- Think about how users might search for your content

**Don't:**

- Create overly specific tags that only apply to one piece of content
- Use tags that are too broad or vague
- Apply tags that don't accurately represent the content
- Use inconsistent naming conventions
- Over-tag content (3-5 relevant tags are usually sufficient)

### Examples of Proper Tagging

#### Example 1: React Tutorial

```yaml
contentTags:
- beginner
- react
- javascript
- tutorial
```

**Rationale:** This is a beginner-friendly tutorial about React (JavaScript framework), so it gets skill level, technology, and format tags.

#### Example 2: Career Guide

```yaml
contentTags:
- intermediate
- career
- guide
```

**Rationale:** This is an intermediate-level career development guide, covering the topic and format categories.

#### Example 3: Advanced Testing Resource

```yaml
contentTags:
- advanced
- testing
- javascript
- reference
```

**Rationale:** This is an advanced reference for JavaScript testing, covering skill level, topic, technology, and format.

#### Example 4: Open Source Contribution Tips

```yaml
contentTags:
- intermediate
- open-source
- git
- tips
```

**Rationale:** This provides intermediate-level tips for contributing to open source projects using Git, covering multiple relevant categories.

### Tag Maintenance

- **Regular Review**: Tags are reviewed periodically to ensure they remain relevant and useful
- **Community Input**: Suggestions for new tags or modifications are welcome through issues or discussions
- **Documentation Updates**: This documentation is updated when new tags are added or existing ones are modified
- **Migration**: When tags are deprecated or renamed, existing content is updated accordingly

### Questions About Tagging?

If you're unsure about which tags to use for your content, or if you think a new tag might be needed, please:

1. Check existing similar content to see what tags are commonly used
2. Ask in the [discussions board](https://github.com/Virtual-Coffee/virtualcoffee.io/discussions)
3. Create an issue with your tagging question
4. Reach out to maintainers for guidance

Remember: Good tagging helps the entire community find and benefit from your contributions!

## Add yourself to the Contributor's list

We want to thank you so much for contributing to our project. Check out all [contributor's documentation](https://allcontributors.org/docs/en/bot/usage) to learn how to add yourself to our Contributor's list.
93 changes: 93 additions & 0 deletions TAGGING_GUIDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Content Tagging Quick Reference

## 🏷️ Available Tags by Category

### 🎯 Skill Level

- `beginner` - New to programming or topic
- `intermediate` - Some experience, deepening knowledge
- `advanced` - Experienced, mastering complex topics

### 🛠️ Technologies

- `javascript` - JavaScript, ES6+, Node.js
- `typescript` - TypeScript language and patterns
- `react` - React library and ecosystem
- `node` - Node.js runtime and backend
- `css` - CSS styling and layouts
- `html` - HTML markup and standards
- `git` - Version control and workflows

### 📚 Topics

- `career` - Career development and growth
- `open-source` - Contributing and maintaining projects
- `testing` - Testing strategies and frameworks
- `deployment` - CI/CD, hosting, DevOps
- `interviewing` - Technical interviews and prep

### 📖 Format

- `tutorial` - Step-by-step instructions
- `reference` - Quick lookup guides
- `guide` - Comprehensive walkthroughs
- `tips` - Short, actionable advice

## ✅ Tagging Best Practices

### Do:

- Use 3-5 relevant tags maximum
- Apply tags from different categories
- Use lowercase, hyphenated naming
- Consider your audience's skill level
- Think about how users will search

### Don't:

- Create overly specific tags
- Use tags that don't match content
- Over-tag (more than 5 tags)
- Use inconsistent naming
- Apply vague or broad tags

## 📝 Example Tagging

```yaml
# React Tutorial for Beginners
contentTags:
- beginner
- react
- javascript
- tutorial

# Career Development Guide
contentTags:
- intermediate
- career
- guide

# Advanced Testing Reference
contentTags:
- advanced
- testing
- javascript
- reference
```

## 🔄 Adding New Tags

1. **Propose** - Create issue/discussion explaining the need
2. **Discuss** - Get community feedback
3. **Implement** - Update `src/util/tagCategories.ts` once approved

## ❓ Need Help?

- Check existing similar content for tag patterns
- Ask in [discussions](https://github.com/Virtual-Coffee/virtualcoffee.io/discussions)
- Create an issue with your question
- Reach out to maintainers

---

_For detailed guidelines, see [CONTRIBUTING.md#content-tagging-system](CONTRIBUTING.md#content-tagging-system)_
23 changes: 20 additions & 3 deletions src/components/PostList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@ import { MdxFile } from '@/util/loadMdx.server';
import Link from 'next/link';
import path from 'path';
import { ReactNode } from 'react';
import TagBadge from './TagBadge';

/*
PostListItem is each resource under a section of content on the homepage.
*/
type PostListItem = {
export type PostListItem = {
href?: string;
title: string;
description?: string | ReactNode;
children?: PostListItem[] | null;
contentTags?: string[];
};

type TitleProps = {
Expand Down Expand Up @@ -41,7 +43,21 @@ export default function PostList({ items }: { items: PostListItem[] | null }) {
{items.map((item, key) => {
return (
<li className="postlist-item" key={key}>
<PostListItemTitle item={item} />
<div className="postlist-header">
<PostListItemTitle item={item} />
{item.contentTags && item.contentTags.length > 0 && (
<div className="postlist-tags">
{item.contentTags.map((tag, tagIndex) => (
<TagBadge
key={`${key}-${tagIndex}-${tag}`}
tag={tag}
variant="default"
size="sm"
/>
))}
</div>
)}
</div>
{item.description && (
<p className="postlist-description">{item.description}</p>
)}
Expand All @@ -68,16 +84,17 @@ export function formatFileListItemsForPostList(
if (!items || internalCurLevel >= depth) {
return null;
}

return items.map((item): PostListItem => {
const parts = item.slug.split(path.sep).filter((part) => {
return !!part && part !== 'content';
});


return {
title: item.meta.title,
description: item.meta.description,
href: `/${parts.join('/')}`,
contentTags: item.contentTags || [],
children: formatFileListItemsForPostList(
item.children,
depth,
Expand Down
62 changes: 62 additions & 0 deletions src/components/TagBadge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { useState } from 'react';

interface TagBadgeProps {
tag: string;
isSelected?: boolean;
onClick?: (tag: string, selected: boolean) => void;
variant?: 'default' | 'filter';
size?: 'sm' | 'md';
disabled?: boolean;
}

export default function TagBadge({
tag,
isSelected = false,
onClick,
variant = 'default',
size = 'md',
disabled = false,
}: TagBadgeProps) {
const handleClick = () => {
if (!disabled && onClick) {
onClick(tag, !isSelected);
}
};

const baseClasses = 'badge tag-badge';
const variantClasses = {
default: 'badge-secondary',
filter: isSelected ? 'badge-primary' : 'badge-outline-secondary',
};
const sizeClasses = {
sm: 'tag-badge-sm',
md: '',
};
const interactiveClasses = onClick && !disabled ? 'tag-badge-interactive' : '';
const disabledClasses = disabled ? 'tag-badge-disabled' : '';

const className = [
baseClasses,
variantClasses[variant],
sizeClasses[size],
interactiveClasses,
disabledClasses,
]
.filter(Boolean)
.join(' ');

const TagComponent = onClick && !disabled ? 'button' : 'span';

return (
<TagComponent
type={TagComponent === 'button' ? 'button' : undefined}
className={className}
onClick={handleClick}
disabled={disabled}
aria-pressed={onClick ? isSelected : undefined}
role={onClick ? 'button' : undefined}
>
{tag}
</TagComponent>
);
}
Loading