테마와 블록 작성에 필요한 스킬

A repository of Claude Code skills for building Discourse themes and block components:

https://github.com/discourse/discourse-theme-skills

:toolbox: What’s included

Theme Authoring Skill — covers a comprehensive scope of building a Discourse theme: scaffolding with the discourse_theme CLI, SCSS architecture, viewport library, localization, settings, modifiers, value transformers, icons, and CSS variables. Detailed reference files for icons, variables, and transformers are included separately and can be loaded on demand. SKILL.md

Block Authoring Skill — covers the theme-side of the Blocks API: writing block components with the @block decorator, defining args schemas, rendering blocks into the available core outlets, conditions, container blocks and layout grouping, and integrating theme translations and settings into block args. SKILL.md

Example Theme — a working theme with a custom homepage built with blocks, demonstrating real patterns for outlets, conditions, and layout composition.


:jigsaw: About the Blocks API

The Blocks API is Discourse’s new framework for building modular, composable UI components in themes and plugins. Blocks are Glimmer components registered into named outlets — like homepage-blocks, hero-blocks, or sidebar-discovery — and can be conditionally shown based on route, user, viewport, site settings, or plugin availability.

A key strength of the system is that blocks have small, focused scope and consistent patterns. This makes them well-suited for AI-assisted development: a model with the block skill can scaffold a working block component, register it to an outlet, and wire up conditions in a single pass.

The example theme in this repo demonstrates a homepage that adapts based on what plugins and content are available. Here’s what the basic homepage looks like, with a hero block and a featured topic list:

When additional conditions are met (a featured tag is configured, the Discourse Events plugin is active, and the Discourse Leaderboard plugin is available) additional blocks are conditionally rendered into the layout:

Blocks aren’t limited to the homepage. The example theme also uses the sidebar-blocks-outlet to add a Home link, the sidebar-discovery outlet to add category-specific sidebar content, and a category-banner block at the top of category pages:

The blocks inspector on DevTools shows the outlet labels and block identifiers overlaid on the page. This makes it easy to understand the layout structure and debug what’s rendering where:


:art: Using with a design platform MCP

The skills pair well with design platform MCPs (such as the Penpot or Figma MCP). With one connected, Claude can read component specs and design tokens directly from your design files and implement them using the skill’s conventions. It’s a tighter loop between design and code, especially when working from a structured design system.


:fork_and_knife: Fork and adjust

Some conventions in the skills are more preference than convention, like the SCSS folder architecture. You can fork the repo and adjust the skills to match your own workflow and conventions.


:speech_balloon: Share what you build

Give it a try and let us know how it goes! We’d love to hear how you’re using the skills, what you’ve built with them, and where they fall short. Feedback, corrections, and forks are all welcome.

Will there be a dedicated Blocks topic or is that this?

If it’s the latter, maybe some code snippets could help? Or is what is in the plugin-api.gjs file the current docs?

Thanks.

There’ll still be documentation that covers the full Blocks API, including implementation in core and plugins. For theming with Blocks, the SKILL.md should already touch on all relevant aspects. It’s compact and very readable.

The example theme includes both initializer files and blocks. The initializer files declare the layout per BlockOutlet: discourse-theme-skills/javascripts/discourse/api-initializers at main · discourse/discourse-theme-skills · GitHub.

To me that’s the biggest change when working with themes: We don’t inject custom components directly into the app, as with PluginOutlets. We rather have a few dedicated layout frames now. All blocks that should render in that frame are registered in the same initializer file, even they show under different conditions.

It helps us keep a clean interface between customizations and the core app layouts.

Having some actual fun with this :winking_face_with_tongue: … As other AI design tools it’s really performant for quickly prototyping ideas that would have been too costly to sketch out manually.

Asked for a very brutalist style editorial homepage, with highly unconventional content featured from the community. Got this layout, which indeed has some pretty nice ideas for featured blocks. Most funnily, it named the theme Newspaper from Hell :grinning_face_with_smiling_eyes:

Then asked for something I always wanted to explore, a Japanese-style portal homepage with dense blocks, pastel colors, lots of small animations.. Love this initial take:

And more fun when it’s all moving :carp_streamer: Theme Creator

flushy

Finally!!! Will try it asap to update my experimental theme components :smiley:

Elmo Surrounded by Intense Fire

This is great work and very creative. If we fork and build off this theme are there considerations for the parent not staying updated with Discourse over time? Trying to think how this should be approached.

Thanks for the resources!

Thanks @BrianC!

On the parent staying updated: the skills track Discourse’s theme and Blocks APIs, so as long as we actively use them, they will be kept in sync as the APIs evolve. The example theme is more of a snapshot to demonstrate patterns. If you fork it, you own your fork. But you can refer back to the skills or new examples when updating your theme.

A central goal for the Blocks API itself is a stable, small surface area, that helps keep customizations resilient across Discourse updates. So If you mostly add custom blocks (as the example theme does) you should already operate within a stable environment. The main thing to watch for would be changes to outlet names or block API signatures. Right now the API is still considered experimental, so there might be changes to names etc.

I’d put the recommended approach like this: fork the theme freely, and lean on the skills docs as the living reference for how things should be done going forward.

I’m just starting to mess around with this (without agentic coding).

I get the impression that it wouldn’t take too much to convert this into a Theme Component which controls just the Homepage of a site - for example, one already using the Horizon Theme. Would that be stupid?

Also, I’ve noticed a couple of issues:

Upcoming Events Block doesn’t sort topics

It simply dumps event topics by creation date; this is most unhelpful!!

ask.discourse.com suggests this type of change to fix this, which I can confirm works (forgive my lack of critical human thought):

@bind
async fetchEvents() {
  const count = this.args.count || 5;
  const results = await ajax("discourse-post-event/events");

  if (!results.events?.length) {
    return null;
  }

  const now = new Date();

  // Separate past and future events, then sort ascending by start date
  const upcoming = results.events
    .filter((e) => new Date(e.starts_at) >= now)
    .sort((a, b) => new Date(a.starts_at) - new Date(b.starts_at));

  return upcoming.slice(0, count);
}

Category Banners Block doesn’t respect the settings

It displays on all categories (not just the specified ones), and doesn’t seem to refresh on navigation (only on a page refresh).

Thanks for giving it a try @nathank! It’s still considered experimental and we will make changes to the API, so I wouldn’t build a homepage-builder-type theme component on top of it for now.

The demo blocks are just basic examples. We also have some API changes coming up that will improve how we can load data. I’ll push changes to the all the blocks on the demo theme once this is available.

The category banners block should show on all categories. I think the category picker you reference is for the featured categories block on the homepage.

드디어 용기를 내어 이것을 조금씩 사용해보았습니다. 공유해 주셔서 정말 감사합니다.

이미 사용자 아바타와 이름(이 부분은 이미 구현했습니다)을 표시하는 커스텀 블록을 생성하는 것은 어떻게 하면 될지 궁금합니다. 여기에 더해, 해당 사용자의 토픽 및 게시물 수, 좋아요 수, 체어 포인트, 그리고 See TL3 Progress 와 유사하지만 특정 배지(그 배지를 기반으로 구축된 커스텀 신뢰 수준)와 관련된 정보도 함께 표시하고 싶습니다.

역할극 게임에서 캐릭터 카드에 경험치(EXP), 기본 정보, 주요 스킬을 볼 수 있는 것처럼요.

블록을 추가하고 싶다면 코어에 PR을 제출해야 할 것 같습니다.

Blocks API에 대해 공부하고 있는데, ask.discourse가 꽤 도움이 되었습니다.

메타 카테고리 배너와 아이콘, 그리고 몇 가지 다른 아이디어를 참고(모방)해서 구현해 보고 싶습니다.

읽어 본 자료 대부분은 자체 호스팅(self-hosted) 사이트에 초점이 맞춰져 있고, 호스팅된 사이트(hosted sites)에 대한 내용은 많지 않습니다.

호스팅된 사이트에는 어떤 제한 사항이 있나요?

이것은 완전히 가능하며, 공유된 테마의 스킬과 예시 블록을 사용하는 에이전트는 이를 코딩하는 데 완전히 능숙할 것입니다.

실제로 저도 얼마 전에 비슷한 블록을 만들었습니다. 아직 새로운 Blocks API를 사용하지는 않지만, Manuel Kostka / Discourse / Blocks / User Profile · GitLab 에서 접근 방식을 살펴볼 수 있습니다. 예를 들어 Canvas Central 테마에서는 다음과 같이 보입니다:

우리는 아직 코어에 블록이 없습니다(아직). 모든 블록은 테마 또는 테마 구성 요소를 사용하여 추가됩니다.

테마와 테마 구성 요소를 사용하여 기존 BlockOutlets에 블록을 추가할 수 있으며, 커스텀 테마를 추가하려면 Pro 플랜 이상이어야 한다고 생각합니다. 그 외에는 제한이 없어야 합니다.

지금 저는 메타에서 아직 실험 단계라 사용하지 말라고 강하게 주장하는, 그리고 ‘도그푸딩’ 중이라고 말하는 AI 봇과 논쟁 중입니다. 더 성숙해지길 기다리려고 포기할 참입니다.

봇은 공식 발표에서 방향성을 얻고 있을 가능성이 높습니다: Creating a 'Blocks' API for injecting content

하지만 동의합니다. 지금은 프로덕션 환경에서 사용하지 않는 것이 좋습니다. 사전 공지 없이 깨지는 변경 사항(breaking changes)이 발생할 가능성이 높기 때문입니다.

스테이징 사이트에서 만지작거리는 거라, 프로덕션 환경에서는 함부로 건드리지 않을 거예요.

그래도 괜찮을 것입니다. 단, 아직 포괄적이지 않다는 것이 아니라, 다른 안정화된 API나 인터페이스와 달리 깨질 수 있는 변경 사항에 대해 헤지하지 않겠다는 점에 주의해야 합니다.

아, 맞다. 내 실수였네. 블록 위치를 추가하는 것에 대한 언급인 줄 알았어.

네, BlockOutlets은 코어에 포함되어 있습니다. 다만 플러그인을 통해서도 추가할 수 있습니다.