임베드된 댓글에도 자동 다크 모드가 적용되나요?

Automatic Dark Mode color scheme switching 은 훌륭한 기능 추가인데, 통합해 주셔서 감사합니다!

(자동) 색상 스킴 전환 기능이 사용 가능해지면서, 블로그에 댓글로 내장된 Discourse의 라이트 모드와 다크 모드 간에 수동으로 전환할 수 있는 방법이 있을지 궁금합니다. 구체적으로, 제 Ghost 블로그에는 사용자가 클릭하여 라이트 모드와 다크 모드를 수동으로 전환할 수 있는 토글이 있습니다. 제 블로그의 CSS는 Discourse iframe 내부의 CSS에 영향을 미칠 수 없다는 점을 알고 있지만, 이번 새로운 기능 덕분에 토글을 통해 Discourse의 색상 스킴도 함께 전환할 수 있는 다른 방법이 있을지 궁금합니다. 댓글이 있는 게시글의 예시는 이 게시글 하단에서 확인하실 수 있습니다.

11개의 좋아요

이것은 가능하지만 다소 까다로운 작업입니다. 현재까지 임베디드 코멘트 엔드포인트에 다크 모드 자동 전환 기능이 추가되지 않았습니다. 이 기능을 추가할 수는 있으며, 브라우저가 다크 모드로 전환될 때 Ghost 사이트가 자동으로 다크 모드로 전환되는 경우라면 작동할 것입니다. 하지만 제가 파악한 바로는, 질문자님의 Ghost 사이트는 다크 모드 버튼을 사용하고 있어 Discourse의 구현 방식과 호환되지 않습니다.

질문자님의 특정 상황을 고려할 때, 버튼 클릭 시 Discourse 임베디드 코멘트 iframe에 클래스를 토글하고, 해당 클래스를 사용하여 임베디드 스타일시트에서 색상을 전환하는 방식으로 처리할 수 있습니다.

13개의 좋아요

좋습니다. 저는 현재 동료들을 위해 다크 모드를 활용하는 여러 (Ghost) 테마를 만들고 있는데, 모두 동일한 Discourse 인스턴스를 공유하게 됩니다. 하지만 그건 아직 좀 더 먼 미래의 일입니다.

불행히도 여기서 무엇을 제안하고 계신지 정확히 이해하지 못하겠습니다. 현재 블로그에서 다크 모드 스타일링을 적용하려는 모든 요소는 해당 항목 앞에 body.dark를 붙여 CSS가 중복 정의되어 있습니다. 예를 들면 다음과 같습니다.

p {
    color: #000;
}
body.dark p {
    color: #FFF;
}

혹시 Discourse iframe 안의 요소를 변경하기 위해 그 body.dark 코드를 사용하라는 제안이신가요? 저는 Discourse의 ‘Embedded CSS’ 필드에 다음 코드를 삽입해 보았지만, 안타깝게도 효과가 없었습니다:

.FF2F-discourse p {
    color: #000;
}
body.dark .FF2F-discourse p {
    color: #FFF;
}

만약 그렇다면, iframe 내부의 특정 클래스를 표적으로 삼아 토글이 별도의 변경 사항을 일으키도록 별도의 JS를 작성하라는 제안이신 건가요? 이전 댓글에서 말씀드린 대로, 외부 코드가 iframe 내부의 CSS에 영향을 미칠 수 없다고 생각했기 때문에 혼란스럽습니다. 하지만 저는 HTML/CSS 초보자일 뿐이므로, 제가 더 잘 알고 있거나 다른 것을 오해하고 있는 것이 아니라면 다행이겠지만, 그렇지 않을 가능성도 충분히 있습니다.

힌트를 주셔서 다시 한번 감사합니다.

7개의 좋아요

네, 정확히 그런 제안입니다. 블로그의 스타일시트는 iframe에 적용될 수 없지만, JS를 사용하여 iframe의 html 또는 body 요소에서 dark 클래스를 토글하고, 그에 따라 Discourse 임베디드 스타일시트를 업데이트할 수 있습니다.

11개의 좋아요

디스코urs 임베드에서 라이트 모드와 다크 모드로 전환하는 방법에 대해 설명하는 두 개의 페이지를 찾았습니다. 그런데 제 JS를 작성하는 개발자가 제가 제안하는 방법이 postMessage를 사용하는 것인지(제가 찾은 페이지들에는 그렇게 나와 있습니다) 아니면 다른 방식인지 궁금해하더군요.

여기 postMessage를 사용한 크로스 윈도우 메시징 구현 방법을 설명하는 페이지가 있습니다.

그리고 이 페이지는 실제로 postMessage를 통해 iframe 내부의 CSS를 변경하는 튜토리얼이며, 특히 라이트 모드와 다크 모드 전환에 관한 내용입니다:

https://cobwwweb.com/change-css-iframe

이 모든 것이 올바른 방향일까요?

8개의 좋아요

죄송합니다, 여기서 답변을 깜빡했습니다. 네, postMessage가 사용 사례에 적합할 수 있다고 생각합니다.

11개의 좋아요

안녕하세요,

이렇게 오래된 주제를 꺼내는 것이라 죄송하지만, 임베디드 댓글 엔드포인트에 대해 다크 모드의 자동 전환 기능이 구현될 계획이 있는지 궁금합니다.

1개의 좋아요

현재 이 기능을 추가할 계획은 없습니다. 사이트에서는 임시 조치로 내장된 스타일시트에 다크 모드 스타일을 추가하거나, 위에서 논의한 postMessage 기법을 사용할 수 있습니다.

7개의 좋아요

postMessage는 message 이벤트를 처리하기 위해 임베디드 iframe이 필요합니다.
이벤트를 처리하는 코드를 iframe에 어디에 추가해야 하나요?

2개의 좋아요

postMessage를 사용하여 구현했습니다. 코드와 실행 로직은 다음과 같습니다:

코드 블록 ①은 임베디드 Discourse iframe에 이벤트 리스너를 추가하여, 임베디드 Discourse가 로드되면 임베디드 Discourse iframe이 포함된 내 웹사이트로 메시지를 전송하도록 합니다.

내 웹사이트가 임베디드 Discourse에서 메시지를 수신하면 코드 블록 ②에 표시된 바와 같이 유효성 검사를 수행하고, 통과하면 setIframeStyle 함수를 호출하여 임베디드 Discourse를 설정합니다.

코드 블록 ③에 표시된 setIframeStyle 함수는 postMessage를 호출하여 iframe에 색상 모드(“dark” 또는 “light”)를 전달합니다. 또한 다크 모드가 전환되면 이 함수가 호출되어 임베디드 Discourse가 내 웹사이트와 동일한 색상 모드를 유지할 수 있도록 합니다.

코드 블록 ④는 임베디드 Discourse가 내 웹사이트에서 전송된 색상 모드 메시지를 처리할 수 있도록 합니다. 여기서는 body 태그의 클래스 이름을 전환하여 색상 모드를 토글합니다.

추가로, 코드 블록 ①과 ④는 다음과 같이 Discourse 관리자 페이지를 통해 추가됩니다:

그리고 사용자 정의 CSS 클래스는 다음과 같이 추가해야 합니다:

12개의 좋아요

공식 테마가 아니기 때문에 "임베디드 CSS"를 수정할 수 없는 경우, 어디서 수정해야 하는지 아시는 분 계신가요?

2개의 좋아요

새 컴포넌트를 만들고 그 내장(Embedded) 탭을 사용하면 되나요?

3개의 좋아요

위 개요를 작성해 주셔서 감사합니다, @mikeguo. 정말 훌륭하게 설명해 주셨네요!

또한, 이 작업을 수행하려면 최신 버전의 Discourse를 사용해야 한다는 점에 유의해 주세요. 내장 헤더(Embedded Header) 기능은 며칠 전에야 추가되었습니다.

8개의 좋아요

수정: Structure of themes and theme components 이 작업을 수행해야 했네요

이것이 잘못되었나요?

const handleMessageListener = (event:MessageEvent<any>)

2개의 좋아요

여전히 이 코드를 활성화할 수 없습니다 (!) 혹시 이 코드를 수정해 주실 수 있을까요?

아무도 이 코드를 활성화할 수 없고, 거의 모든 사람이 강제 밝기를 사용한다는 걸 보고 있어요. 우리 눈에 아주 나쁘네요 :frowning:

1개의 좋아요

위 방법이 여전히 작동한다는 것을 확인했습니다. 다만 스크린샷에서 코드를 직접 입력하지 않아도 되었으면 좋았을 텐데 말이죠 :slight_smile: 그래서 약간 업데이트한 코드를 여기에 올립니다:

단계

  1. 댓글 iframe이 렌더링을 완료하면 메인 브라우저 윈도우에 이를 알리는 메시지를 전송합니다.
  2. 브라우저가 현재 다크/라이트 모드 설정을 조회하여 해당 값을 iframe으로 되돌려 보냅니다.
  3. iframe이 메시지를 수신하고, 다크/라이트 모드 설정에 따라 data-attribute, 클래스 또는 유사한 속성을 설정합니다.

코드

  1. iframe이 로드되면 부모 윈도우에 알림을 전송합니다. 이 코드는 Discourse에서 Admin -> Customize -> (테마 선택) -> Edit CSS/HTML -> Embedded Header 아래에 입력해야 합니다.
<script type="text/javascript">
    window.addEventListener("load", (event) => {
        window.parent.postMessage("iframe loaded", "*");
    }, false);
</script>
  1. 메인 윈도우에서 이 수신 트리거를 처리합니다. 이 코드는 자신의 블로그 사이트에 위치해야 합니다:
<script type="text/javascript">
  const discourse_url = "https://your.discourse-instance.org";

  // 여기서 테마를 결정하고, iframe에 테마가 무엇인지 알리는 메시지를 전송합니다.
  // notifyFrameStyle 연결 방법은 아래를 참조하세요.
  const notifyIFrameOfTheme = () => {
    const iframe = document.getElementById("discourse-embed-frame");
    if (iframe && iframe.contentWindow) {
      iframe.contentWindow.postMessage(
        {
          // 다크 모드 설정을 저장하는 방식에 따라 아래 줄을 수정하여 설정을 가져오세요.
          theme: document.documentElement.getAttribute("data-theme")
        },
        discourse_url
      );
    }
  };

  // "iframe loaded" 메시지를 수신할 때 setFrameStyle을 호출합니다.
  const handleMessageListener = (event) => {
    var origin = event.origin;
    if ((origin === discourse_url) && (event.data == "iframe loaded")) {
      notifyIFrameOfTheme();
    }
  };
</script>
  1. (1)의 <script> 블록에 notifyFrameStyle이 전송하는 테마 메시지 리스너를 추가합니다:
    window.addEventListener("message", (event) => {
        const payload = event.data;
        if (payload.theme) {
          // 테마 설정으로 무언가를 수행합니다. 저는 iframe의 <html>에 `data-theme` 속성을 설정했지만,
          // 클래스 속성이나 유사한 것을 설정하는 것이 더 나을 수도 있습니다.
          document.documentElement.setAttribute("data-theme", payload.theme);
        }
    }, false);

스타일링

Admin -> Customize -> (테마 선택) -> Edit CSS/HTML -> Embedded CSS에서 이제 각 모드별로 CSS를 제공할 수 있습니다. 예를 들어, Discourse 스타일링 변수를 오버라이드할 수 있습니다:

html[data-theme="dark"] {
  --primary: #ced6dd;
  --primary-low: #48566b;
  --secondary: #14181e;
  --tertiary: #2b7e8d;
}

도움이 되길 바랍니다!

6개의 좋아요

위에서 복사하여 붙여넣을 수 있는 코드에는 임베딩 창에 이벤트 리스너를 추가하는 부분이 빠져 있습니다:

window.addEventListener("message", handleMessageListener);

참고: 이 작업에는 특별한 CORS 설정이 필요하지 않습니다.

그 부분을 찾아줘서 고마워! 더 이상 위 게시글을 수정할 수는 없지만, window.addEventListener는 (2) 아래 코드 스니펫의 맨 아래에 위치해야 해.

이 실제 예시를 참고해 봐.

6개의 좋아요