chatscope / chat-ui-kit-styles

Base theme for chat-ui-kit-react
MIT License
15 stars 18 forks source link

Documentation for custom styles #5

Open chloe-schoreisz opened 3 years ago

chloe-schoreisz commented 3 years ago

I would love to see some documentations on how to stylized the different components (eg. colors, fonts, change of icons, etc)

chloe-schoreisz commented 3 years ago

For anyone looking, we figured out a way: We created our own main.scss file, that looks like this:

@charset "utf-8";
@import "./variables";
@import "./helpers/mixins";
@import "./helpers/functions";
@import "@chatscope/chat-ui-kit-styles/themes/default/main.scss";

This will work if your app supports scss. You should compile this to css if it doesn't.

supersnager commented 3 years ago

@chloe-schoreisz Thank you for your explanation. It's awesome that somebody helps me maintaining the project :)

And here is the solution that I use in my projects based on Create React App.

Assuming your app supports scss (e.g. CRA, NEXTjs).

project_root/src/themes/default/_chat-overrides.scss:

/* The path depends on the current location of the scss file */
@import "../../../node_modules/@chatscope/chat-ui-kit-styles/themes/default/variables";

// Here you can override variables from the imported file above. Check its source for a list of available variables
$main-container-border: 0;
$color-text: red;
$conversation-color: pink;

// ... and so on 

project_root/src/themes/default/main.scss:

@charset "utf-8";
@import "./chat-overrides";
@import "../../../node_modules/@chatscope/chat-ui-kit-styles/themes/default/main";

// ... any other imports 

project_root/src/App.js:

import './themes/default/main.scss';
import MyApp from "./MyApp"

function App() {
 return (<MyApp />);
}
export default App;
supersnager commented 3 years ago

@chloe-schoreisz

I'm still getting issues for stylizing the send button and the input box

What's your issue with styling these items??

chloe-schoreisz commented 3 years ago

I was trying to obtain a specific color for the SendButton through the custom scss file. I could not change it from there, and I tried all the variables. The only way I was able to change the color was by implementing my own CustomMessageInput, and hardcode the color in the styling.

// CustomMessageInput.tsx

                         <MessageInput
                ref={...}
                onSend={sendMessage}
                onChange={(_: string, textContent: string) => {
                    setMessageInputValue(textContent);
                }}
                value={messageInputValue}
                sendButton={false}
                attachButton={false}
            />
            <SendButton
                onClick={() => sendMessage(messageInputValue)}
                style={{
                    color: '#0C3276',
                    backgroundColor: '#C7FED2',
                    width: '48px',
                    height: '45px'

                }}
            />
chloe-schoreisz commented 3 years ago

I was also trying to update the icon of the send button, but couldn't find a way

supersnager commented 3 years ago

@chloe-schoreisz Your solution is fine, especially when there is a need to make more changes than simply color change.

However, the send button color should be changeable by overriding $send-button-color variable https://github.com/chatscope/chat-ui-kit-styles/blob/6823363a99ef2a6be7eaf6fadd17599b4ad99138/themes/default/_variables.scss#L180 but now I see there is a bug :sweat_smile: (missing !default) I will fix fixed it in here: https://github.com/chatscope/chat-ui-kit-styles/issues/8

sh1ggy commented 1 year ago

Just gave this a try in NextJS with my own project and ran into some issues with the styles not applying despite following the instructions that @supersnager laid out so thought it would be good to share here. The import of the variables.scss file in your custom.scss styles should be happening after your actual custom variables are assigned. We referred to this article to figure this out. Unfortunately, we weren't able to get modular styles to work (in attempts to follow NextJS conventions), having to import and apply the styles globally on _app.tsx.

$color-text: red;

@import "../../../node_modules/@chatscope/chat-ui-kit-styles/themes/default/variables";
@import "../../../node_modules/@chatscope/chat-ui-kit-styles/themes/default/main";

Not sure if this is a quirk of NextJS, but would be interested to know. Thank you!

axelferdinand commented 1 year ago
/* The path depends on the current location of the scss file */
@import "../../../node_modules/@chatscope/chat-ui-kit-styles/themes/default/variables";

// Here you can override variables from the imported file above. Check its source for a list of available variables
$main-container-border: 0;
$color-text: red;
$conversation-color: pink;

Using this approach, I manage to change some of the variables (i.e. the default font family), but only if i replace !default with !important, and that's not possible with all variables (i.e. the color variables – they throw an error if I use !important – do you know a way around?

supersnager commented 1 year ago

@axelferdinand Please share the code (e.g. your _chat-overrides.scss ), how do you override values? !default and !important are used for different things, when overriding, you don't use !default.

axelferdinand commented 1 year ago

@supersnager Sure!

_chat-overrides.scss

// Default font family
//

$default-font-family: 'Clan', Helvetica, sans-serif !important;

// Colors
//

$color-primary: #000 !default;
$color-primary-light: #FFF !default;
$color-primary-dark: #000 !default;
$color-primary-dark: #000 !default;

main.scss

@charset "utf-8";

@font-face {
  font-family: 'Clan';
  src: url('fonts/ClanOffc-News.woff2') format('woff2');
       font-weight: normal;
       font-style: normal;
}

@font-face {
  font-family: 'Clan';
  src: url('fonts/ClanOffc-Bold.woff2') format('woff2');
       font-weight: bold;
       font-style: bold;
}

@import "./chat-overrides";
@import "../../../node_modules/@chatscope/chat-ui-kit-styles/themes/default/main";

_app.txt

import type { AppProps } from "next/app";
import { FpjsProvider } from "@fingerprintjs/fingerprintjs-pro-react";
import { configureAbly } from "@ably-labs/react-hooks";
import '../themes/default/main.scss';

const prefix = process.env.API_ROOT || "";

const clientId =
  Math.random().toString(36).substring(2, 15) +
  Math.random().toString(36).substring(2, 15);

configureAbly({
  authUrl: `${prefix}/api/createTokenRequest?clientId=${clientId}`,
  clientId: clientId,
});

const fpjsPublicApiKey = process.env.FINGERPRINT as string;

export default function App({ Component, pageProps }: AppProps) {
  return (
    <FpjsProvider
      loadOptions={{
        apiKey: fpjsPublicApiKey,
      }}
    >
      <Component {...pageProps} />
    </FpjsProvider>
  );
}

index.txs

import Head from "next/head";
import { useState } from "react";
import { useVisitorData } from "@fingerprintjs/fingerprintjs-pro-react";
import ReactMarkdown from "react-markdown";
import * as timeago from "timeago.js";

import styles from "@chatscope/chat-ui-kit-styles/dist/default/styles.min.css";

import {
  MainContainer,
  ChatContainer,
  MessageList,
  Message,
  MessageInput,
  ConversationHeader,
  TypingIndicator,
} from "@chatscope/chat-ui-kit-react";

import { useChannel } from "@ably-labs/react-hooks";
import { Types } from "ably";

type ConversationEntry = {
  message: string;
  speaker: "bot" | "user";
  date: Date;
  id?: string;
};

type request = {
  prompt: string;
};

const updateChatbotMessage = (
  conversation: ConversationEntry[],
  message: Types.Message
): ConversationEntry[] => {
  const interactionId = message.data.interactionId;

  const updatedConversation = conversation.reduce(
    (acc: ConversationEntry[], e: ConversationEntry) => [
      ...acc,
      e.id === interactionId
        ? { ...e, message: e.message + message.data.token }
        : e,
    ],
    []
  );

  return conversation.some((e) => e.id === interactionId)
    ? updatedConversation
    : [
        ...updatedConversation,
        {
          id: interactionId,
          message: message.data.token,
          speaker: "bot",
          date: new Date(),
        },
      ];
};

export default function Home() {
  const [text, setText] = useState("");
  const [extendedResult, updateExtendedResult] = useState(false);
  const [conversation, setConversation] = useState<ConversationEntry[]>([]);
  const [botIsTyping, setBotIsTyping] = useState(false);
  const [statusMessage, setStatusMessage] = useState("Skriv spørsmålet ditt...");

  const { isLoading, data: visitorData } = useVisitorData(
    { extendedResult },
    { immediate: true }
  );

  useChannel(visitorData?.visitorId! || "default", (message) => {
    switch (message.data.event) {
      case "response":
        setConversation((state) => updateChatbotMessage(state, message));
        break;
      case "status":
        setStatusMessage(message.data.message);
        break;
      case "responseEnd":
      default:
        setBotIsTyping(false);
        setStatusMessage("Waiting for query...");
    }
  });

  const submit = async () => {
    setConversation((state) => [
      ...state,
      {
        message: text,
        speaker: "user",
        date: new Date(),
      },
    ]);
    try {
      setBotIsTyping(true);
      const response = await fetch("/api/chat", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ prompt: text, userId: visitorData?.visitorId }),
      });

      await response.json();
    } catch (error) {
      console.error("Error submitting message:", error);
    } finally {
      setBotIsTyping(false);
    }
    setText("");
  };

  return (
    <>
      <Head>
        <title>Virke</title>
        <meta name="description" content="Generated by create next app" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <main className={styles.main}>
        <div
          style={{ position: "relative", height: "98vh", overflow: "hidden" }}
        >
          <MainContainer>
            <ChatContainer>
              <ConversationHeader>
                <ConversationHeader.Actions></ConversationHeader.Actions>
                <ConversationHeader.Content
                  userName="Få hjelp med det juridiske innholdet på virke.no"
                  info={statusMessage}
                />
              </ConversationHeader>

              <MessageList
                typingIndicator={
                  botIsTyping ? (
                    <TypingIndicator content="Vi jobber med saken..." />
                  ) : null
                }
              >
                {conversation.map((entry, index) => {
                  return (
                    <Message
                      key={index}
                      style={{ width: "90%" }}
                      model={{
                        type: "custom",
                        sender: entry.speaker,
                        position: "single",
                        direction:
                          entry.speaker === "bot" ? "incoming" : "outgoing",
                      }}
                    >
                      <Message.CustomContent>
                        <ReactMarkdown>{entry.message}</ReactMarkdown>
                      </Message.CustomContent>
                      <Message.Footer
                        sentTime={timeago.format(entry.date)}
                        sender={entry.speaker === "bot" ? "Virke" : "Du"}
                      />
                    </Message>
                  );
                })}
              </MessageList>
              <MessageInput
                placeholder="Skriv her..."
                onSend={submit}
                onChange={(e, text) => {
                  setText(text);
                }}
                sendButton={true}
                autoFocus
                disabled={isLoading}
              />
            </ChatContainer>
          </MainContainer>
        </div>
      </main>
    </>
  );
}
supersnager commented 1 year ago

@axelferdinand

Try to remove !default in _chat-overrides.scss

Change this:

$color-primary: #000 !default;
$color-primary-light: #FFF !default;
$color-primary-dark: #000 !default;
$color-primary-dark: #000 !default;

to this:

$color-primary: #000;
$color-primary-light: #FFF;
$color-primary-dark: #000;
$color-primary-dark: #000;
axelferdinand commented 1 year ago

@supersnager I tried that, but it didn't work, unfortunately.

My console says this:

wait  - compiling...
event - compiled successfully in 426 ms (434 modules)

...but the colors (or other variables) doesn't update. This is the only thing that will update:

$default-font-family: 'Clan', Helvetica, sans-serif !important;

supersnager commented 1 year ago

@axelferdinand I think I have found the source of the problem.

Try to remove the following line from the index.tsx:

import styles from "@chatscope/chat-ui-kit-styles/dist/default/styles.min.css";

and remove className from:

<main className={styles.main}>

Your example is not working because you import main.scss which includes:

@import "./chat-overrides";
@import "../../../node_modules/@chatscope/chat-ui-kit-styles/themes/default/main";

and next you import @chatscope/chat-ui-kit-styles/dist/default/styles.min.css The import of the minimized styles overrides a theme imported earlier.

After importing main.scss all chatscope styles are ready because the theme is built in your app. So importing @chatscope/chat-ui-kit-styles/dist/default/styles.min.css is not necessary.

BTW in the @chatscope/chat-ui-kit-styles/dist/default/styles.min.css there is no "main" class.

axelferdinand commented 1 year ago

@supersnager That did indeed work!! Thank you so much!!

dolce-emmy commented 1 year ago

Hello I was wondering if there is any way possible to style the chat-ui-kit using the tailwind? I tried with the MainContainer, ChatContainer, MessageList and it worked... However when I tried to style the MessageInput and the MessageSeperator using tailwind I couldn't... so I was wondering if you can help me with that please?

supersnager commented 1 year ago

@dolce-emmy What exactly do you want to do?

abuvanth commented 1 year ago

I want dark theme

FatGuyy commented 1 year ago

I want dark theme

Me too. It'd be nice to just add a dark mode functionality in the kit itself @supersnager

hhussein97 commented 11 months ago

Any updated guide on how to change the style?

ragib1803 commented 8 months ago

Just take these classnames to your styles.css and change accordingly. This overrides the default styles.

div .cs-message-list__typing-indicator-container div { color: #6d4700; }

div .cs-main-container, div .cs-chat-container .cs-message-input { border: 0px; }

div .cs-message--incoming .cs-messagecontent, div .cs-chat-container .cs-message-input, div .psthumb-y, div .psrail-y:hover .psthumb-y, div .psrail-y:active .psthumb-y { background-color: #ffc436; }

div .cs-message-inputcontent-editor, div .cs-message-inputcontent-editor-wrapper, div .cs-message-list, div .psrail-y:hover, div .ps .psrail-y:hover, div .ps__rail-y { background-color: #ffebbb; }

div .cs-message--outgoing .cs-message__content { background-color: #ffa552; }

div .cs-typing-indicator__dot { background-color: #6d4700; }

jasonhargrove commented 2 months ago

Next.js

// src/components/chat.scss

@charset "utf-8";

$color-text: white;
$blue: #2894CD;
$green: #61AD4A;
$message-content-incoming-bg-color: $blue;
$message-content-outgoing-bg-color: #444;
$color-secondary: #555;
$message-input-bg-color: white;
$message-input-content-editor-wrapper-bg-color: white;
$message-input-content-editor-color: black;
$button-attachment-color: white;
$button-send-color: $green;
$message-group-header-padding: 0.5rem;
$message-list-scroll-wrapper-padding: 1.2em 1.2em 0 0.8em;

@import "@chatscope/chat-ui-kit-styles/themes/default/variables";

@import "@chatscope/chat-ui-kit-styles/themes/default/main";

In your chat component:

import './chat.scss';
Thauanny commented 1 month ago

I'm trying to use it on backstage and any of those solutions work at all